diff options
168 files changed, 3478 insertions, 1295 deletions
diff --git a/Android.mk b/Android.mk index 13b717adce27..49dc6af164d6 100644 --- a/Android.mk +++ b/Android.mk @@ -433,6 +433,7 @@ include external/junit/Common.mk non_base_dirs := \ ../../external/apache-http/src/org/apache/http \ + ../opt/telephony/src/java/android/provider \ ../opt/telephony/src/java/android/telephony \ ../opt/telephony/src/java/android/telephony/gsm \ ../opt/net/voip/src/java/android/net/rtp \ diff --git a/api/current.txt b/api/current.txt index 4f398cfd6ced..ff72c4a6de63 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33,6 +33,7 @@ package android { field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER"; field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH"; field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN"; + field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED"; field public static final java.lang.String BRICK = "android.permission.BRICK"; field public static final java.lang.String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED"; field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS"; @@ -3972,7 +3973,6 @@ package android.app { } public static class Notification.Action implements android.os.Parcelable { - ctor public Notification.Action(); ctor public Notification.Action(int, java.lang.CharSequence, android.app.PendingIntent); method public android.app.Notification.Action clone(); method public int describeContents(); @@ -3988,7 +3988,6 @@ package android.app { ctor public Notification.BigPictureStyle(android.app.Notification.Builder); method public android.app.Notification.BigPictureStyle bigLargeIcon(android.graphics.Bitmap); method public android.app.Notification.BigPictureStyle bigPicture(android.graphics.Bitmap); - method public android.app.Notification build(); method public android.app.Notification.BigPictureStyle setBigContentTitle(java.lang.CharSequence); method public android.app.Notification.BigPictureStyle setSummaryText(java.lang.CharSequence); } @@ -3997,7 +3996,6 @@ package android.app { ctor public Notification.BigTextStyle(); ctor public Notification.BigTextStyle(android.app.Notification.Builder); method public android.app.Notification.BigTextStyle bigText(java.lang.CharSequence); - method public android.app.Notification build(); method public android.app.Notification.BigTextStyle setBigContentTitle(java.lang.CharSequence); method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence); } @@ -4042,14 +4040,13 @@ package android.app { ctor public Notification.InboxStyle(); ctor public Notification.InboxStyle(android.app.Notification.Builder); method public android.app.Notification.InboxStyle addLine(java.lang.CharSequence); - method public android.app.Notification build(); method public android.app.Notification.InboxStyle setBigContentTitle(java.lang.CharSequence); method public android.app.Notification.InboxStyle setSummaryText(java.lang.CharSequence); } public static abstract class Notification.Style { ctor public Notification.Style(); - method public abstract android.app.Notification build(); + method public android.app.Notification build(); method protected void checkBuilder(); method protected android.widget.RemoteViews getStandardView(int); method protected void internalSetBigContentTitle(java.lang.CharSequence); @@ -5107,6 +5104,7 @@ package android.bluetooth { public final class BluetoothDevice implements android.os.Parcelable { method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback); + method public boolean createBond(); method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException; method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException; method public int describeContents(); @@ -5117,6 +5115,8 @@ package android.bluetooth { method public java.lang.String getName(); method public int getType(); method public android.os.ParcelUuid[] getUuids(); + method public boolean setPairingConfirmation(boolean); + method public boolean setPin(byte[]); method public void writeToParcel(android.os.Parcel, int); field public static final java.lang.String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED"; field public static final java.lang.String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED"; @@ -5125,6 +5125,7 @@ package android.bluetooth { field public static final java.lang.String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED"; field public static final java.lang.String ACTION_FOUND = "android.bluetooth.device.action.FOUND"; field public static final java.lang.String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"; + field public static final java.lang.String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; field public static final java.lang.String ACTION_UUID = "android.bluetooth.device.action.UUID"; field public static final int BOND_BONDED = 12; // 0xc field public static final int BOND_BONDING = 11; // 0xb @@ -5139,9 +5140,13 @@ package android.bluetooth { field public static final java.lang.String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; field public static final java.lang.String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; field public static final java.lang.String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; + field public static final java.lang.String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; + field public static final java.lang.String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; field public static final java.lang.String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; field public static final java.lang.String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2 + field public static final int PAIRING_VARIANT_PIN = 0; // 0x0 } public final class BluetoothGatt implements android.bluetooth.BluetoothProfile { @@ -10908,7 +10913,6 @@ package android.hardware.camera2 { method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException; method public abstract void flush() throws android.hardware.camera2.CameraAccessException; method public abstract java.lang.String getId(); - method public abstract android.hardware.camera2.CameraCharacteristics getProperties() throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException; @@ -20993,7 +20997,6 @@ package android.provider { field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type"; field public static final java.lang.String COLUMN_SIZE = "_size"; field public static final java.lang.String COLUMN_SUMMARY = "summary"; - field public static final int FLAG_DIR_HIDE_GRID_TITLES = 64; // 0x40 field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10 field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20 field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8 @@ -21010,18 +21013,12 @@ package android.provider { field public static final java.lang.String COLUMN_ICON = "icon"; field public static final java.lang.String COLUMN_MIME_TYPES = "mime_types"; field public static final java.lang.String COLUMN_ROOT_ID = "root_id"; - field public static final java.lang.String COLUMN_ROOT_TYPE = "root_type"; field public static final java.lang.String COLUMN_SUMMARY = "summary"; field public static final java.lang.String COLUMN_TITLE = "title"; - field public static final int FLAG_ADVANCED = 4; // 0x4 - field public static final int FLAG_EMPTY = 32; // 0x20 field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 - field public static final int FLAG_SUPPORTS_RECENTS = 8; // 0x8 - field public static final int FLAG_SUPPORTS_SEARCH = 16; // 0x10 - field public static final int ROOT_TYPE_DEVICE = 3; // 0x3 - field public static final int ROOT_TYPE_SERVICE = 1; // 0x1 - field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2 + field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4 + field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8 } public abstract class DocumentsProvider extends android.content.ContentProvider { @@ -21739,6 +21736,277 @@ package android.provider { method public static void update(android.content.ContentProviderClient, android.net.Uri, byte[]) throws android.os.RemoteException; } + public final class Telephony { + } + + public static abstract interface Telephony.BaseMmsColumns implements android.provider.BaseColumns { + field public static final java.lang.String CONTENT_CLASS = "ct_cls"; + field public static final java.lang.String CONTENT_LOCATION = "ct_l"; + field public static final java.lang.String CONTENT_TYPE = "ct_t"; + field public static final java.lang.String DATE = "date"; + field public static final java.lang.String DATE_SENT = "date_sent"; + field public static final java.lang.String DELIVERY_REPORT = "d_rpt"; + field public static final java.lang.String DELIVERY_TIME = "d_tm"; + field public static final java.lang.String EXPIRY = "exp"; + field public static final java.lang.String LOCKED = "locked"; + field public static final java.lang.String MESSAGE_BOX = "msg_box"; + field public static final int MESSAGE_BOX_ALL = 0; // 0x0 + field public static final int MESSAGE_BOX_DRAFTS = 3; // 0x3 + field public static final int MESSAGE_BOX_INBOX = 1; // 0x1 + field public static final int MESSAGE_BOX_OUTBOX = 4; // 0x4 + field public static final int MESSAGE_BOX_SENT = 2; // 0x2 + field public static final java.lang.String MESSAGE_CLASS = "m_cls"; + field public static final java.lang.String MESSAGE_ID = "m_id"; + field public static final java.lang.String MESSAGE_SIZE = "m_size"; + field public static final java.lang.String MESSAGE_TYPE = "m_type"; + field public static final java.lang.String MMS_VERSION = "v"; + field public static final java.lang.String PRIORITY = "pri"; + field public static final java.lang.String READ = "read"; + field public static final java.lang.String READ_REPORT = "rr"; + field public static final java.lang.String READ_STATUS = "read_status"; + field public static final java.lang.String REPORT_ALLOWED = "rpt_a"; + field public static final java.lang.String RESPONSE_STATUS = "resp_st"; + field public static final java.lang.String RESPONSE_TEXT = "resp_txt"; + field public static final java.lang.String RETRIEVE_STATUS = "retr_st"; + field public static final java.lang.String RETRIEVE_TEXT = "retr_txt"; + field public static final java.lang.String RETRIEVE_TEXT_CHARSET = "retr_txt_cs"; + field public static final java.lang.String SEEN = "seen"; + field public static final java.lang.String STATUS = "st"; + field public static final java.lang.String SUBJECT = "sub"; + field public static final java.lang.String SUBJECT_CHARSET = "sub_cs"; + field public static final java.lang.String TEXT_ONLY = "text_only"; + field public static final java.lang.String THREAD_ID = "thread_id"; + field public static final java.lang.String TRANSACTION_ID = "tr_id"; + } + + public static abstract interface Telephony.CanonicalAddressesColumns implements android.provider.BaseColumns { + field public static final java.lang.String ADDRESS = "address"; + } + + public static final class Telephony.Carriers implements android.provider.BaseColumns { + field public static final java.lang.String APN = "apn"; + field public static final java.lang.String AUTH_TYPE = "authtype"; + field public static final java.lang.String BEARER = "bearer"; + field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled"; + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String CURRENT = "current"; + field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC"; + field public static final java.lang.String MCC = "mcc"; + field public static final java.lang.String MMSC = "mmsc"; + field public static final java.lang.String MMSPORT = "mmsport"; + field public static final java.lang.String MMSPROXY = "mmsproxy"; + field public static final java.lang.String MNC = "mnc"; + field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data"; + field public static final java.lang.String MVNO_TYPE = "mvno_type"; + field public static final java.lang.String NAME = "name"; + field public static final java.lang.String NUMERIC = "numeric"; + field public static final java.lang.String PASSWORD = "password"; + field public static final java.lang.String PORT = "port"; + field public static final java.lang.String PROTOCOL = "protocol"; + field public static final java.lang.String PROXY = "proxy"; + field public static final java.lang.String ROAMING_PROTOCOL = "roaming_protocol"; + field public static final java.lang.String SERVER = "server"; + field public static final java.lang.String TYPE = "type"; + field public static final java.lang.String USER = "user"; + } + + public static final class Telephony.Mms implements android.provider.Telephony.BaseMmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + field public static final android.net.Uri REPORT_REQUEST_URI; + field public static final android.net.Uri REPORT_STATUS_URI; + } + + public static final class Telephony.Mms.Addr implements android.provider.BaseColumns { + field public static final java.lang.String ADDRESS = "address"; + field public static final java.lang.String CHARSET = "charset"; + field public static final java.lang.String CONTACT_ID = "contact_id"; + field public static final java.lang.String MSG_ID = "msg_id"; + field public static final java.lang.String TYPE = "type"; + } + + public static final class Telephony.Mms.Draft implements android.provider.Telephony.BaseMmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Mms.Inbox implements android.provider.Telephony.BaseMmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Mms.Intents { + field public static final java.lang.String CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED"; + field public static final java.lang.String DELETED_CONTENTS = "deleted_contents"; + } + + public static final class Telephony.Mms.Outbox implements android.provider.Telephony.BaseMmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Mms.Part implements android.provider.BaseColumns { + field public static final java.lang.String CHARSET = "chset"; + field public static final java.lang.String CONTENT_DISPOSITION = "cd"; + field public static final java.lang.String CONTENT_ID = "cid"; + field public static final java.lang.String CONTENT_LOCATION = "cl"; + field public static final java.lang.String CONTENT_TYPE = "ct"; + field public static final java.lang.String CT_START = "ctt_s"; + field public static final java.lang.String CT_TYPE = "ctt_t"; + field public static final java.lang.String FILENAME = "fn"; + field public static final java.lang.String MSG_ID = "mid"; + field public static final java.lang.String NAME = "name"; + field public static final java.lang.String SEQ = "seq"; + field public static final java.lang.String TEXT = "text"; + field public static final java.lang.String _DATA = "_data"; + } + + public static final class Telephony.Mms.Rate { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String SENT_TIME = "sent_time"; + } + + public static final class Telephony.Mms.Sent implements android.provider.Telephony.BaseMmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.MmsSms implements android.provider.BaseColumns { + field public static final android.net.Uri CONTENT_CONVERSATIONS_URI; + field public static final android.net.Uri CONTENT_DRAFT_URI; + field public static final android.net.Uri CONTENT_FILTER_BYPHONE_URI; + field public static final android.net.Uri CONTENT_LOCKED_URI; + field public static final android.net.Uri CONTENT_UNDELIVERED_URI; + field public static final android.net.Uri CONTENT_URI; + field public static final int ERR_TYPE_GENERIC = 1; // 0x1 + field public static final int ERR_TYPE_GENERIC_PERMANENT = 10; // 0xa + field public static final int ERR_TYPE_MMS_PROTO_PERMANENT = 12; // 0xc + field public static final int ERR_TYPE_MMS_PROTO_TRANSIENT = 3; // 0x3 + field public static final int ERR_TYPE_SMS_PROTO_PERMANENT = 11; // 0xb + field public static final int ERR_TYPE_SMS_PROTO_TRANSIENT = 2; // 0x2 + field public static final int ERR_TYPE_TRANSPORT_FAILURE = 4; // 0x4 + field public static final int MMS_PROTO = 1; // 0x1 + field public static final int NO_ERROR = 0; // 0x0 + field public static final android.net.Uri SEARCH_URI; + field public static final int SMS_PROTO = 0; // 0x0 + field public static final java.lang.String TYPE_DISCRIMINATOR_COLUMN = "transport_type"; + } + + public static final class Telephony.MmsSms.PendingMessages implements android.provider.BaseColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DUE_TIME = "due_time"; + field public static final java.lang.String ERROR_CODE = "err_code"; + field public static final java.lang.String ERROR_TYPE = "err_type"; + field public static final java.lang.String LAST_TRY = "last_try"; + field public static final java.lang.String MSG_ID = "msg_id"; + field public static final java.lang.String MSG_TYPE = "msg_type"; + field public static final java.lang.String PROTO_TYPE = "proto_type"; + field public static final java.lang.String RETRY_INDEX = "retry_index"; + } + + public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + method public static java.lang.String getDefaultSmsPackage(android.content.Context); + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Sms.Conversations implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + field public static final java.lang.String MESSAGE_COUNT = "msg_count"; + field public static final java.lang.String SNIPPET = "snippet"; + } + + public static final class Telephony.Sms.Draft implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Sms.Inbox implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Sms.Intents { + method public static android.telephony.SmsMessage[] getMessagesFromIntent(android.content.Intent); + field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.provider.Telephony.ACTION_CHANGE_DEFAULT"; + field public static final java.lang.String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "package"; + field public static final int RESULT_SMS_DUPLICATED = 5; // 0x5 + field public static final int RESULT_SMS_GENERIC_ERROR = 2; // 0x2 + field public static final int RESULT_SMS_HANDLED = 1; // 0x1 + field public static final int RESULT_SMS_OUT_OF_MEMORY = 3; // 0x3 + field public static final int RESULT_SMS_UNSUPPORTED = 4; // 0x4 + field public static final java.lang.String SIM_FULL_ACTION = "android.provider.Telephony.SIM_FULL"; + field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED"; + field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER"; + field public static final java.lang.String SMS_EMERGENCY_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED"; + field public static final java.lang.String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED"; + field public static final java.lang.String SMS_REJECTED_ACTION = "android.provider.Telephony.SMS_REJECTED"; + field public static final java.lang.String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION = "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED"; + field public static final java.lang.String WAP_PUSH_DELIVER_ACTION = "android.provider.Telephony.WAP_PUSH_DELIVER"; + field public static final java.lang.String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED"; + } + + public static final class Telephony.Sms.Outbox implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Telephony.Sms.Sent implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns { + field public static final android.net.Uri CONTENT_URI; + field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static abstract interface Telephony.TextBasedSmsColumns { + field public static final java.lang.String ADDRESS = "address"; + field public static final java.lang.String BODY = "body"; + field public static final java.lang.String DATE = "date"; + field public static final java.lang.String DATE_SENT = "date_sent"; + field public static final java.lang.String ERROR_CODE = "error_code"; + field public static final java.lang.String LOCKED = "locked"; + field public static final int MESSAGE_TYPE_ALL = 0; // 0x0 + field public static final int MESSAGE_TYPE_DRAFT = 3; // 0x3 + field public static final int MESSAGE_TYPE_FAILED = 5; // 0x5 + field public static final int MESSAGE_TYPE_INBOX = 1; // 0x1 + field public static final int MESSAGE_TYPE_OUTBOX = 4; // 0x4 + field public static final int MESSAGE_TYPE_QUEUED = 6; // 0x6 + field public static final int MESSAGE_TYPE_SENT = 2; // 0x2 + field public static final java.lang.String PERSON = "person"; + field public static final java.lang.String PROTOCOL = "protocol"; + field public static final java.lang.String READ = "read"; + field public static final java.lang.String REPLY_PATH_PRESENT = "reply_path_present"; + field public static final java.lang.String SEEN = "seen"; + field public static final java.lang.String SERVICE_CENTER = "service_center"; + field public static final java.lang.String STATUS = "status"; + field public static final int STATUS_COMPLETE = 0; // 0x0 + field public static final int STATUS_FAILED = 64; // 0x40 + field public static final int STATUS_NONE = -1; // 0xffffffff + field public static final int STATUS_PENDING = 32; // 0x20 + field public static final java.lang.String SUBJECT = "subject"; + field public static final java.lang.String THREAD_ID = "thread_id"; + field public static final java.lang.String TYPE = "type"; + } + + public static final class Telephony.Threads implements android.provider.Telephony.ThreadsColumns { + field public static final int BROADCAST_THREAD = 1; // 0x1 + field public static final int COMMON_THREAD = 0; // 0x0 + field public static final android.net.Uri CONTENT_URI; + field public static final android.net.Uri OBSOLETE_THREADS_URI; + } + + public static abstract interface Telephony.ThreadsColumns implements android.provider.BaseColumns { + field public static final java.lang.String DATE = "date"; + field public static final java.lang.String ERROR = "error"; + field public static final java.lang.String HAS_ATTACHMENT = "has_attachment"; + field public static final java.lang.String MESSAGE_COUNT = "message_count"; + field public static final java.lang.String READ = "read"; + field public static final java.lang.String RECIPIENT_IDS = "recipient_ids"; + field public static final java.lang.String SNIPPET = "snippet"; + field public static final java.lang.String SNIPPET_CHARSET = "snippet_cs"; + field public static final java.lang.String TYPE = "type"; + } + public class UserDictionary { ctor public UserDictionary(); field public static final java.lang.String AUTHORITY = "user_dictionary"; @@ -31585,6 +31853,7 @@ package android.widget { method public void setAnimationStyle(int); method public void setBackgroundDrawable(android.graphics.drawable.Drawable); method public void setContentWidth(int); + method public void setDropDownGravity(int); method public void setHeight(int); method public void setHorizontalOffset(int); method public void setInputMethodMode(int); @@ -31766,6 +32035,7 @@ package android.widget { public class PopupMenu { ctor public PopupMenu(android.content.Context, android.view.View); + ctor public PopupMenu(android.content.Context, android.view.View, int); method public void dismiss(); method public android.view.View.OnTouchListener getDragToOpenListener(); method public android.view.Menu getMenu(); @@ -31829,6 +32099,7 @@ package android.widget { method public void setWindowLayoutMode(int, int); method public void showAsDropDown(android.view.View); method public void showAsDropDown(android.view.View, int, int); + method public void showAsDropDown(android.view.View, int, int, int); method public void showAtLocation(android.view.View, int, int, int); method public void update(); method public void update(int, int); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e70ad1ca8afa..c63e586a7127 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -432,28 +432,119 @@ public class Notification implements Parcelable /** * Additional semantic data to be carried around with this Notification. + * <p> + * The extras keys defined here are intended to capture the original inputs to {@link Builder} + * APIs, and are intended to be used by + * {@link android.service.notification.NotificationListenerService} implementations to extract + * detailed information from notification objects. */ public Bundle extras = new Bundle(); - // extras keys for Builder inputs + /** + * {@link #extras} key: this is the title of the notification, + * as supplied to {@link Builder#setContentTitle(CharSequence)}. + */ public static final String EXTRA_TITLE = "android.title"; + + /** + * {@link #extras} key: this is the title of the notification when shown in expanded form, + * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}. + */ public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big"; + + /** + * {@link #extras} key: this is the main text payload, as supplied to + * {@link Builder#setContentText(CharSequence)}. + */ public static final String EXTRA_TEXT = "android.text"; + + /** + * {@link #extras} key: this is a third line of text, as supplied to + * {@link Builder#setSubText(CharSequence)}. + */ public static final String EXTRA_SUB_TEXT = "android.subText"; + + /** + * {@link #extras} key: this is a small piece of additional text as supplied to + * {@link Builder#setContentInfo(CharSequence)}. + */ public static final String EXTRA_INFO_TEXT = "android.infoText"; + + /** + * {@link #extras} key: this is a line of summary information intended to be shown + * alongside expanded notifications, as supplied to (e.g.) + * {@link BigTextStyle#setSummaryText(CharSequence)}. + */ public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; + + /** + * {@link #extras} key: this is the resource ID of the notification's main small icon, as + * supplied to {@link Builder#setSmallIcon(int)}. + */ public static final String EXTRA_SMALL_ICON = "android.icon"; + + /** + * {@link #extras} key: this is a bitmap to be used instead of the small icon when showing the + * notification payload, as + * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}. + */ public static final String EXTRA_LARGE_ICON = "android.largeIcon"; + + /** + * {@link #extras} key: this is a bitmap to be used instead of the one from + * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is + * shown in its expanded form, as supplied to + * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}. + */ public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big"; + + /** + * {@link #extras} key: this is the progress value supplied to + * {@link Builder#setProgress(int, int, boolean)}. + */ public static final String EXTRA_PROGRESS = "android.progress"; + + /** + * {@link #extras} key: this is the maximum value supplied to + * {@link Builder#setProgress(int, int, boolean)}. + */ public static final String EXTRA_PROGRESS_MAX = "android.progressMax"; + + /** + * {@link #extras} key: whether the progress bar is indeterminate, supplied to + * {@link Builder#setProgress(int, int, boolean)}. + */ public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; + + /** + * {@link #extras} key: whether {@link #when} should be shown as a count-up timer (specifically + * a {@link android.widget.Chronometer}) instead of a timestamp, as supplied to + * {@link Builder#setUsesChronometer(boolean)}. + */ public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; + + /** + * {@link #extras} key: whether {@link #when} should be shown, + * as supplied to {@link Builder#setShowWhen(boolean)}. + */ public static final String EXTRA_SHOW_WHEN = "android.showWhen"; + + /** + * {@link #extras} key: this is a bitmap to be shown in {@link BigPictureStyle} expanded + * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}. + */ public static final String EXTRA_PICTURE = "android.picture"; + + /** + * {@link #extras} key: An array of CharSequences to show in {@link InboxStyle} expanded + * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}. + */ public static final String EXTRA_TEXT_LINES = "android.textLines"; - // extras keys for other interesting pieces of information + /** + * {@link #extras} key: An array of people that this notification relates to, specified + * by contacts provider contact URI. + */ public static final String EXTRA_PEOPLE = "android.people"; /** @@ -464,38 +555,53 @@ public class Notification implements Parcelable public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; /** - * Notification extra to specify heads up display preference. + * Not used. * @hide */ public static final String EXTRA_AS_HEADS_UP = "headsup"; /** - * Value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is not appropriate. + * Value for {@link #EXTRA_AS_HEADS_UP}. * @hide */ public static final int HEADS_UP_NEVER = 0; /** - * Default value for {@link #EXTRA_AS_HEADS_UP} indicating that heads up display is appropriate. + * Default value for {@link #EXTRA_AS_HEADS_UP}. * @hide */ public static final int HEADS_UP_ALLOWED = 1; /** - * Value for {@link #EXTRA_AS_HEADS_UP} that advocates for heads up display. + * Value for {@link #EXTRA_AS_HEADS_UP}. * @hide */ public static final int HEADS_UP_REQUESTED = 2; /** - * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification. + * Structure to encapsulate a named action that can be shown as part of this notification. + * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is + * selected by the user. + * <p> + * Apps should use {@link Builder#addAction(int, CharSequence, PendingIntent)} to create and + * attach actions. */ public static class Action implements Parcelable { + /** + * Small icon representing the action. + */ public int icon; + /** + * Title of the action. + */ public CharSequence title; + /** + * Intent to send when the user invokes this action. May be null, in which case the action + * may be rendered in a disabled presentation by the system UI. + */ public PendingIntent actionIntent; - @SuppressWarnings("unused") - public Action() { } + + private Action() { } private Action(Parcel in) { icon = in.readInt(); title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); @@ -503,16 +609,20 @@ public class Notification implements Parcelable actionIntent = PendingIntent.CREATOR.createFromParcel(in); } } - public Action(int icon_, CharSequence title_, PendingIntent intent_) { - this.icon = icon_; - this.title = title_; - this.actionIntent = intent_; + /** + * Use {@link Builder#addAction(int, CharSequence, PendingIntent)}. + */ + public Action(int icon, CharSequence title, PendingIntent intent) { + this.icon = icon; + this.title = title; + this.actionIntent = intent; } + @Override public Action clone() { return new Action( this.icon, - this.title.toString(), + this.title, this.actionIntent // safe to alias ); } @@ -542,6 +652,12 @@ public class Notification implements Parcelable }; } + /** + * Array of all {@link Action} structures attached to this notification by + * {@link Builder#addAction(int, CharSequence, PendingIntent)}. Mostly useful for instances of + * {@link android.service.notification.NotificationListenerService} that provide an alternative + * interface for invoking actions. + */ public Action[] actions; /** @@ -1468,8 +1584,15 @@ public class Notification implements Parcelable /** * Add an action to this notification. Actions are typically displayed by * the system as a button adjacent to the notification content. - * <br> - * A notification displays up to 3 actions, from left to right in the order they were added. + * <p> + * Every action must have an icon (32dp square and matching the + * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo + * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}. + * <p> + * A notification in its expanded form can display up to 3 actions, from left to right in + * the order they were added. Actions will not be displayed when the notification is + * collapsed, however, so be sure that any essential functions may be accessed by the user + * in some other way (for example, in the Activity pointed to by {@link #contentIntent}). * * @param icon Resource ID of a drawable that represents the action. * @param title Text describing the action. @@ -1666,8 +1789,9 @@ public class Notification implements Parcelable /** * Apply the unstyled operations and return a new {@link Notification} object. + * @hide */ - private Notification buildUnstyled() { + public Notification buildUnstyled() { Notification n = new Notification(); n.when = mWhen; n.icon = mSmallIcon; @@ -1745,12 +1869,10 @@ public class Notification implements Parcelable * object. */ public Notification build() { - final Notification n; + Notification n = buildUnstyled(); if (mStyle != null) { - n = mStyle.build(); - } else { - n = buildUnstyled(); + n = mStyle.buildStyled(n); } n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); @@ -1860,7 +1982,21 @@ public class Notification implements Parcelable } } - public abstract Notification build(); + /** + * @hide + */ + public abstract Notification buildStyled(Notification wip); + + /** + * Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is + * attached to. + * + * @return the fully constructed Notification. + */ + public Notification build() { + checkBuilder(); + return mBuilder.build(); + } } /** @@ -1946,10 +2082,11 @@ public class Notification implements Parcelable extras.putParcelable(EXTRA_PICTURE, mPicture); } + /** + * @hide + */ @Override - public Notification build() { - checkBuilder(); - Notification wip = mBuilder.buildUnstyled(); + public Notification buildStyled(Notification wip) { if (mBigLargeIconSet ) { mBuilder.mLargeIcon = mBigLargeIcon; } @@ -2039,10 +2176,11 @@ public class Notification implements Parcelable return contentView; } + /** + * @hide + */ @Override - public Notification build() { - checkBuilder(); - Notification wip = mBuilder.buildUnstyled(); + public Notification buildStyled(Notification wip) { wip.bigContentView = makeBigContentView(); wip.extras.putCharSequence(EXTRA_TEXT, mBigText); @@ -2150,10 +2288,11 @@ public class Notification implements Parcelable return contentView; } + /** + * @hide + */ @Override - public Notification build() { - checkBuilder(); - Notification wip = mBuilder.buildUnstyled(); + public Notification buildStyled(Notification wip) { wip.bigContentView = makeBigContentView(); return wip; diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 2c85382fe13c..1efdc8186442 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -219,7 +219,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}, * {@link #BOND_BONDING}, * {@link #BOND_BONDED}. - */ + */ public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; /** * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. @@ -228,7 +228,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}, * {@link #BOND_BONDING}, * {@link #BOND_BONDED}. - */ + */ public static final String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; /** @@ -253,12 +253,26 @@ public final class BluetoothDevice implements Parcelable { */ public static final int BOND_BONDED = 12; - /** @hide */ + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents for unbond reason. + * @hide + */ public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; - /** @hide */ + + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents to indicate pairing method used. Possible values are: + * {@link #PAIRING_VARIANT_PIN}, + * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION}, + */ public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; - /** @hide */ + + /** + * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} + * intents as the value of passkey. + */ public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; /** @@ -306,7 +320,10 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_NAME_FAILED = "android.bluetooth.device.action.NAME_FAILED"; - /** @hide */ + /** + * Broadcast Action: This intent is used to broadcast PAIRING REQUEST + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; @@ -446,8 +463,8 @@ public final class BluetoothDevice implements Parcelable { public static final int UNBOND_REASON_REMOVED = 9; /** - * The user will be prompted to enter a pin - * @hide + * The user will be prompted to enter a pin or + * a privileged app will enter a pin for user. */ public static final int PAIRING_VARIANT_PIN = 0; @@ -458,8 +475,8 @@ public final class BluetoothDevice implements Parcelable { public static final int PAIRING_VARIANT_PASSKEY = 1; /** - * The user will be prompted to confirm the passkey displayed on the screen - * @hide + * The user will be prompted to confirm the passkey displayed on the screen or + * a privileged app will confirm the passkey for the user. */ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; @@ -707,10 +724,9 @@ public final class BluetoothDevice implements Parcelable { * the bonding process completes, and its result. * <p>Android system services will handle the necessary user interactions * to confirm and complete the bonding process. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return false on immediate error, true if bonding will begin - * @hide */ public boolean createBond() { if (sService == null) { @@ -946,7 +962,13 @@ public final class BluetoothDevice implements Parcelable { return BluetoothDevice.ERROR; } - /** @hide */ + /** + * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @return true pin has been set + * false for error + */ public boolean setPin(byte[] pin) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); @@ -968,7 +990,13 @@ public final class BluetoothDevice implements Parcelable { return false; } - /** @hide */ + /** + * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @return true confirmation has been sent out + * false for error + */ public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index ec8904144acf..a9a72b074df8 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -16,11 +16,9 @@ package android.hardware.camera2; -import android.view.Surface; import android.os.Handler; -import android.util.Log; +import android.view.Surface; -import java.lang.AutoCloseable; import java.util.List; /** @@ -127,24 +125,11 @@ public interface CameraDevice extends AutoCloseable { * @return the ID for this camera device * * @see CameraManager#getCameraCharacteristics - * @see CameraManager#getDeviceIdList + * @see CameraManager#getCameraIdList */ public String getId(); /** - * Get the static properties for this camera. These are identical to the - * properties returned by {@link CameraManager#getCameraCharacteristics}. - * - * @return the static properties of the camera - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device has been closed - * - * @see CameraManager#getCameraCharacteristics - */ - public CameraCharacteristics getProperties() throws CameraAccessException; - /** * <p>Set up a new output set of Surfaces for the camera device.</p> * * <p>The configuration determines the set of potential output Surfaces for diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index f126472ec6b9..70a6f441ed0e 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -88,24 +88,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public CameraCharacteristics getProperties() throws CameraAccessException { - - CameraMetadataNative info = new CameraMetadataNative(); - - try { - mRemoteDevice.getCameraInfo(/*out*/info); - } catch(CameraRuntimeException e) { - throw e.asChecked(); - } catch(RemoteException e) { - // impossible - return null; - } - - CameraCharacteristics properties = new CameraCharacteristics(info); - return properties; - } - - @Override public void configureOutputs(List<Surface> outputs) throws CameraAccessException { synchronized (mLock) { HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 4044b311b422..3bd515b810df 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -45,6 +45,7 @@ interface IPrintManager { void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener, int userId); + List<PrintServiceInfo> getInstalledPrintServices(int userId); List<PrintServiceInfo> getEnabledPrintServices(int userId); void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl index 291e81f6f554..96b168d3908a 100644 --- a/core/java/android/print/IPrintSpooler.aidl +++ b/core/java/android/print/IPrintSpooler.aidl @@ -36,7 +36,6 @@ import android.print.PrintJobInfo; */ oneway interface IPrintSpooler { void removeObsoletePrintJobs(); - void forgetPrintJobs(in List<PrintJobId> printJob); void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName, int state, int appId, int sequence); void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback, diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl index 0cf00cc267bb..827081227385 100644 --- a/core/java/android/print/IPrintSpoolerClient.aidl +++ b/core/java/android/print/IPrintSpoolerClient.aidl @@ -29,5 +29,5 @@ oneway interface IPrintSpoolerClient { void onPrintJobQueued(in PrintJobInfo printJob); void onAllPrintJobsForServiceHandled(in ComponentName printService); void onAllPrintJobsHandled(); - void onPrintJobStateChanged(in PrintJobId printJobId, int appId); + void onPrintJobStateChanged(in PrintJobInfo printJob); } diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index a01538805eba..0859fdde1744 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -289,7 +289,26 @@ public final class PrintManager { return enabledServices; } } catch (RemoteException re) { - Log.e(LOG_TAG, "Error getting the enalbed print services", re); + Log.e(LOG_TAG, "Error getting the enabled print services", re); + } + return Collections.emptyList(); + } + + /** + * Gets the list of installed print services. + * + * @return The installed service list or an empty list. + * + * @hide + */ + public List<PrintServiceInfo> getInstalledPrintServices() { + try { + List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId); + if (installedServices != null) { + return installedServices; + } + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error getting the installed print services", re); } return Collections.emptyList(); } diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java index c6dbc1664b30..d32b71b9ed56 100644 --- a/core/java/android/print/PrinterDiscoverySession.java +++ b/core/java/android/print/PrinterDiscoverySession.java @@ -28,6 +28,7 @@ import android.util.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; /** @@ -40,8 +41,8 @@ public final class PrinterDiscoverySession { private static final int MSG_PRINTERS_ADDED = 1; private static final int MSG_PRINTERS_REMOVED = 2; - private final ArrayMap<PrinterId, PrinterInfo> mPrinters = - new ArrayMap<PrinterId, PrinterInfo>(); + private final LinkedHashMap<PrinterId, PrinterInfo> mPrinters = + new LinkedHashMap<PrinterId, PrinterInfo>(); private final IPrintManager mPrintManager; @@ -192,22 +193,44 @@ public final class PrinterDiscoverySession { } } - private void handlePrintersAdded(List<PrinterInfo> printers) { + private void handlePrintersAdded(List<PrinterInfo> addedPrinters) { if (isDestroyed()) { return; } - boolean printersChanged = false; - final int addedPrinterCount = printers.size(); - for (int i = 0; i < addedPrinterCount; i++) { - PrinterInfo addedPrinter = printers.get(i); - PrinterInfo oldPrinter = mPrinters.put(addedPrinter.getId(), addedPrinter); - if (oldPrinter == null || !oldPrinter.equals(addedPrinter)) { - printersChanged = true; + + // No old printers - do not bother keeping their position. + if (mPrinters.isEmpty()) { + final int printerCount = addedPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterInfo printer = addedPrinters.get(i); + mPrinters.put(printer.getId(), printer); } - } - if (printersChanged) { notifyOnPrintersChanged(); + return; } + + // Add the printers to a map. + ArrayMap<PrinterId, PrinterInfo> addedPrintersMap = + new ArrayMap<PrinterId, PrinterInfo>(); + final int printerCount = addedPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterInfo printer = addedPrinters.get(i); + addedPrintersMap.put(printer.getId(), printer); + } + + // Update printers we already have. + for (PrinterId oldPrinterId : mPrinters.keySet()) { + PrinterInfo updatedPrinter = addedPrintersMap.remove(oldPrinterId); + if (updatedPrinter != null) { + mPrinters.put(oldPrinterId, updatedPrinter); + } + } + + // Add the new printers, i.e. what is left. + mPrinters.putAll(addedPrintersMap); + + // Announce the change. + notifyOnPrintersChanged(); } private void handlePrintersRemoved(List<PrinterId> printerIds) { diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 4c9af19ab183..85ec80368c2a 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -99,7 +99,7 @@ public final class DocumentsContract { /** * Unique ID of a document. This ID is both provided by and interpreted * by a {@link DocumentsProvider}, and should be treated as an opaque - * value by client applications. + * value by client applications. This column is required. * <p> * Each document must have a unique ID within a provider, but that * single document may be included as a child of multiple directories. @@ -117,7 +117,7 @@ public final class DocumentsContract { * Concrete MIME type of a document. For example, "image/png" or * "application/pdf" for openable files. A document can also be a * directory containing additional documents, which is represented with - * the {@link #MIME_TYPE_DIR} MIME type. + * the {@link #MIME_TYPE_DIR} MIME type. This column is required. * <p> * Type: STRING * @@ -127,15 +127,15 @@ public final class DocumentsContract { /** * Display name of a document, used as the primary title displayed to a - * user. + * user. This column is required. * <p> * Type: STRING */ public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME; /** - * Summary of a document, which may be shown to a user. The summary may - * be {@code null}. + * Summary of a document, which may be shown to a user. This column is + * optional, and may be {@code null}. * <p> * Type: STRING */ @@ -143,9 +143,9 @@ public final class DocumentsContract { /** * Timestamp when a document was last modified, in milliseconds since - * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. A - * {@link DocumentsProvider} can update this field using events from - * {@link OnCloseListener} or other reliable + * January 1, 1970 00:00:00.0 UTC. This column is required, and may be + * {@code null} if unknown. A {@link DocumentsProvider} can update this + * field using events from {@link OnCloseListener} or other reliable * {@link ParcelFileDescriptor} transports. * <p> * Type: INTEGER (long) @@ -155,15 +155,16 @@ public final class DocumentsContract { public static final String COLUMN_LAST_MODIFIED = "last_modified"; /** - * Specific icon resource ID for a document, or {@code null} to use - * platform default icon based on {@link #COLUMN_MIME_TYPE}. + * Specific icon resource ID for a document. This column is optional, + * and may be {@code null} to use a platform-provided default icon based + * on {@link #COLUMN_MIME_TYPE}. * <p> * Type: INTEGER (int) */ public static final String COLUMN_ICON = "icon"; /** - * Flags that apply to a document. + * Flags that apply to a document. This column is required. * <p> * Type: INTEGER (int) * @@ -171,12 +172,13 @@ public final class DocumentsContract { * @see #FLAG_SUPPORTS_DELETE * @see #FLAG_SUPPORTS_THUMBNAIL * @see #FLAG_DIR_PREFERS_GRID - * @see #FLAG_DIR_SUPPORTS_CREATE + * @see #FLAG_DIR_PREFERS_LAST_MODIFIED */ public static final String COLUMN_FLAGS = "flags"; /** - * Size of a document, in bytes, or {@code null} if unknown. + * Size of a document, in bytes, or {@code null} if unknown. This column + * is required. * <p> * Type: INTEGER (long) */ @@ -211,7 +213,7 @@ public final class DocumentsContract { * writability of a document may change over time, for example due to * remote access changes. This flag indicates that a document client can * expect {@link ContentResolver#openOutputStream(Uri)} to succeed. - * + * * @see #COLUMN_FLAGS */ public static final int FLAG_SUPPORTS_WRITE = 1 << 1; @@ -265,8 +267,9 @@ public final class DocumentsContract { * * @see #COLUMN_FLAGS * @see #FLAG_DIR_PREFERS_GRID + * @hide */ - public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 6; + public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 16; } /** @@ -282,31 +285,17 @@ public final class DocumentsContract { /** * Unique ID of a root. This ID is both provided by and interpreted by a * {@link DocumentsProvider}, and should be treated as an opaque value - * by client applications. + * by client applications. This column is required. * <p> * Type: STRING */ public static final String COLUMN_ROOT_ID = "root_id"; /** - * Type of a root, used for clustering when presenting multiple roots to - * a user. + * Flags that apply to a root. This column is required. * <p> * Type: INTEGER (int) * - * @see #ROOT_TYPE_SERVICE - * @see #ROOT_TYPE_SHORTCUT - * @see #ROOT_TYPE_DEVICE - */ - public static final String COLUMN_ROOT_TYPE = "root_type"; - - /** - * Flags that apply to a root. - * <p> - * Type: INTEGER (int) - * - * @see #FLAG_ADVANCED - * @see #FLAG_EMPTY * @see #FLAG_LOCAL_ONLY * @see #FLAG_SUPPORTS_CREATE * @see #FLAG_SUPPORTS_RECENTS @@ -315,22 +304,23 @@ public final class DocumentsContract { public static final String COLUMN_FLAGS = "flags"; /** - * Icon resource ID for a root. + * Icon resource ID for a root. This column is required. * <p> * Type: INTEGER (int) */ public static final String COLUMN_ICON = "icon"; /** - * Title for a root, which will be shown to a user. + * Title for a root, which will be shown to a user. This column is + * required. * <p> * Type: STRING */ public static final String COLUMN_TITLE = "title"; /** - * Summary for this root, which may be shown to a user. The summary may - * be {@code null}. + * Summary for this root, which may be shown to a user. This column is + * optional, and may be {@code null}. * <p> * Type: STRING */ @@ -338,7 +328,7 @@ public final class DocumentsContract { /** * Document which is a directory that represents the top directory of - * this root. + * this root. This column is required. * <p> * Type: STRING * @@ -347,20 +337,20 @@ public final class DocumentsContract { public static final String COLUMN_DOCUMENT_ID = "document_id"; /** - * Number of bytes available in this root, or {@code null} if unknown or - * unbounded. + * Number of bytes available in this root. This column is optional, and + * may be {@code null} if unknown or unbounded. * <p> * Type: INTEGER (long) */ public static final String COLUMN_AVAILABLE_BYTES = "available_bytes"; /** - * MIME types supported by this root, or {@code null} if the root - * supports all MIME types. Multiple MIME types can be separated by a - * newline. For example, a root supporting audio might use - * "audio/*\napplication/x-flac". + * MIME types supported by this root. This column is optional, and if + * {@code null} the root is assumed to support all MIME types. Multiple + * MIME types can be separated by a newline. For example, a root + * supporting audio might return "audio/*\napplication/x-flac". * <p> - * Type: String + * Type: STRING */ public static final String COLUMN_MIME_TYPES = "mime_types"; @@ -368,29 +358,6 @@ public final class DocumentsContract { public static final String MIME_TYPE_ITEM = "vnd.android.document/root"; /** - * Type of root that represents a storage service, such as a cloud-based - * service. - * - * @see #COLUMN_ROOT_TYPE - */ - public static final int ROOT_TYPE_SERVICE = 1; - - /** - * Type of root that represents a shortcut to content that may be - * available elsewhere through another storage root. - * - * @see #COLUMN_ROOT_TYPE - */ - public static final int ROOT_TYPE_SHORTCUT = 2; - - /** - * Type of root that represents a physical storage device. - * - * @see #COLUMN_ROOT_TYPE - */ - public static final int ROOT_TYPE_DEVICE = 3; - - /** * Flag indicating that at least one directory under this root supports * creating content. Roots with this flag will be shown when an * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}. @@ -409,21 +376,13 @@ public final class DocumentsContract { public static final int FLAG_LOCAL_ONLY = 1 << 1; /** - * Flag indicating that this root should only be visible to advanced - * users. - * - * @see #COLUMN_FLAGS - */ - public static final int FLAG_ADVANCED = 1 << 2; - - /** * Flag indicating that this root can report recently modified * documents. * * @see #COLUMN_FLAGS * @see DocumentsContract#buildRecentDocumentsUri(String, String) */ - public static final int FLAG_SUPPORTS_RECENTS = 1 << 3; + public static final int FLAG_SUPPORTS_RECENTS = 1 << 2; /** * Flag indicating that this root supports search. @@ -432,19 +391,31 @@ public final class DocumentsContract { * @see DocumentsProvider#querySearchDocuments(String, String, * String[]) */ - public static final int FLAG_SUPPORTS_SEARCH = 1 << 4; + public static final int FLAG_SUPPORTS_SEARCH = 1 << 3; /** * Flag indicating that this root is currently empty. This may be used * to hide the root when opening documents, but the root will still be * shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is - * also set. + * also set. If the value of this flag changes, such as when a root + * becomes non-empty, you must send a content changed notification for + * {@link DocumentsContract#buildRootsUri(String)}. * * @see #COLUMN_FLAGS - * @see DocumentsProvider#querySearchDocuments(String, String, - * String[]) + * @see ContentResolver#notifyChange(Uri, + * android.database.ContentObserver, boolean) + * @hide + */ + public static final int FLAG_EMPTY = 1 << 16; + + /** + * Flag indicating that this root should only be visible to advanced + * users. + * + * @see #COLUMN_FLAGS + * @hide */ - public static final int FLAG_EMPTY = 1 << 5; + public static final int FLAG_ADVANCED = 1 << 17; } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6c6635dfd531..b8cccb1c5784 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2797,7 +2797,6 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.USB_MASS_STORAGE_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.USE_GOOGLE_MAIL); MOVED_TO_GLOBAL.add(Settings.Global.WEB_AUTOFILL_QUERY_URL); - MOVED_TO_GLOBAL.add(Settings.Global.WIFI_COUNTRY_CODE); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FREQUENCY_BAND); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_IDLE_MS); @@ -5208,12 +5207,6 @@ public final class Settings { "wifi_networks_available_repeat_delay"; /** - * 802.11 country code in ISO 3166 format - * @hide - */ - public static final String WIFI_COUNTRY_CODE = "wifi_country_code"; - - /** * The interval in milliseconds to issue wake up scans when wifi needs * to connect. This is necessary to connect to an access point when * device is on the move and the screen is off. diff --git a/core/java/android/transition/TextChange.java b/core/java/android/transition/ChangeText.java index cf190a1cd66e..b1be70f32eed 100644 --- a/core/java/android/transition/TextChange.java +++ b/core/java/android/transition/ChangeText.java @@ -37,7 +37,7 @@ import java.util.Map; * * @hide */ -public class TextChange extends Transition { +public class ChangeText extends Transition { private static final String LOG_TAG = "TextChange"; @@ -103,7 +103,7 @@ public class TextChange extends Transition { * transition is run. * @return this textChange object. */ - public TextChange setChangeBehavior(int changeBehavior) { + public ChangeText setChangeBehavior(int changeBehavior) { if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) { mChangeBehavior = changeBehavior; } @@ -179,9 +179,13 @@ public class TextChange extends Transition { startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1; } if (!startText.equals(endText)) { - view.setText(startText); - if (view instanceof EditText) { - setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); + final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(startText); + if (view instanceof EditText) { + setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + } } Animator anim; if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) { @@ -200,8 +204,6 @@ public class TextChange extends Transition { }); } else { // Fade out start text - final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); - final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); ValueAnimator outAnim = null, inAnim = null; if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || mChangeBehavior == CHANGE_BEHAVIOR_OUT) { @@ -210,8 +212,8 @@ public class TextChange extends Transition { @Override public void onAnimationUpdate(ValueAnimator animation) { int currAlpha = (Integer) animation.getAnimatedValue(); - view.setTextColor(currAlpha << 24 | Color.red(startColor) << 16 | - Color.green(startColor) << 8 | Color.red(startColor)); + view.setTextColor(currAlpha << 24 | startColor & 0xff0000 | + startColor & 0xff00 | startColor & 0xff); } }); outAnim.addListener(new AnimatorListenerAdapter() { @@ -225,6 +227,8 @@ public class TextChange extends Transition { endSelectionEnd); } } + // restore opaque alpha and correct end color + view.setTextColor(endColor); } }); } @@ -239,6 +243,13 @@ public class TextChange extends Transition { Color.green(endColor) << 8 | Color.red(endColor)); } }); + inAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + // restore opaque alpha and correct end color + view.setTextColor(endColor); + } + }); } if (outAnim != null && inAnim != null) { anim = new AnimatorSet(); @@ -251,21 +262,32 @@ public class TextChange extends Transition { } } TransitionListener transitionListener = new TransitionListenerAdapter() { - boolean mCanceled = false; + int mPausedColor = 0; @Override public void onTransitionPause(Transition transition) { - view.setText(endText); - if (view instanceof EditText) { - setSelection(((EditText) view), endSelectionStart, endSelectionEnd); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(endText); + if (view instanceof EditText) { + setSelection(((EditText) view), endSelectionStart, endSelectionEnd); + } + } + if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { + mPausedColor = view.getCurrentTextColor(); + view.setTextColor(endColor); } } @Override public void onTransitionResume(Transition transition) { - view.setText(startText); - if (view instanceof EditText) { - setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { + view.setText(startText); + if (view instanceof EditText) { + setSelection(((EditText) view), startSelectionStart, startSelectionEnd); + } + } + if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { + view.setTextColor(mPausedColor); } } }; diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java index 5f948bd1e5df..8edb1ff74405 100644 --- a/core/java/android/transition/Fade.java +++ b/core/java/android/transition/Fade.java @@ -30,6 +30,24 @@ import android.view.ViewGroup; * {@link View#setVisibility(int)} state of the view as well as whether it * is parented in the current view hierarchy. * + * <p>The ability of this transition to fade out a particular view, and the + * way that that fading operation takes place, is based on + * the situation of the view in the view hierarchy. For example, if a view was + * simply removed from its parent, then the view will be added into a {@link + * android.view.ViewGroupOverlay} while fading. If a visible view is + * changed to be {@link View#GONE} or {@link View#INVISIBLE}, then the + * visibility will be changed to {@link View#VISIBLE} for the duration of + * the animation. However, if a view is in a hierarchy which is also altering + * its visibility, the situation can be more complicated. In general, if a + * view that is no longer in the hierarchy in the end scene still has a + * parent (so its parent hierarchy was removed, but it was not removed from + * its parent), then it will be left alone to avoid side-effects from + * improperly removing it from its parent. The only exception to this is if + * the previous {@link Scene} was + * {@link Scene#getSceneForLayout(android.view.ViewGroup, int, android.content.Context) + * created from a layout resource file}, then it is considered safe to un-parent + * the starting scene view in order to fade it out.</p> + * * <p>A Fade transition can be described in a resource file by using the * tag <code>fade</code>, along with the standard * attributes of {@link android.R.styleable#Fade} and @@ -167,7 +185,7 @@ public class Fade extends Visibility { if ((mFadingMode & OUT) != OUT) { return null; } - View view; + View view = null; View startView = (startValues != null) ? startValues.view : null; View endView = (endValues != null) ? endValues.view : null; if (DBG) { @@ -177,9 +195,28 @@ public class Fade extends Visibility { View overlayView = null; View viewToKeep = null; if (endView == null || endView.getParent() == null) { - // view was removed: add the start view to the Overlay - view = startView; - overlayView = view; + if (endView != null) { + // endView was removed from its parent - add it to the overlay + view = overlayView = endView; + } else if (startView != null) { + // endView does not exist. Use startView only under certain + // conditions, because placing a view in an overlay necessitates + // it being removed from its current parent + if (startView.getParent() == null) { + // no parent - safe to use + view = overlayView = startView; + } else if (startView.getParent() instanceof View && + startView.getParent().getParent() == null) { + View startParent = (View) startView.getParent(); + int id = startParent.getId(); + if (id != View.NO_ID && sceneRoot.findViewById(id) != null && mCanRemoveViews) { + // no parent, but its parent is unparented but the parent + // hierarchy has been replaced by a new hierarchy with the same id + // and it is safe to un-parent startView + view = overlayView = startView; + } + } + } } else { // visibility change if (endVisibility == View.INVISIBLE) { diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java index f81eeef153b5..d798abec5a33 100644 --- a/core/java/android/transition/Scene.java +++ b/core/java/android/transition/Scene.java @@ -157,11 +157,11 @@ public final class Scene { public void enter() { // Apply layout change, if any - if (mLayoutId >= 0 || mLayout != null) { + if (mLayoutId > 0 || mLayout != null) { // empty out parent container before adding to it getSceneRoot().removeAllViews(); - if (mLayoutId >= 0) { + if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); @@ -242,4 +242,19 @@ public final class Scene { mExitAction = action; } + + /** + * Returns whether this Scene was created by a layout resource file, determined + * by the layoutId passed into + * {@link #getSceneForLayout(android.view.ViewGroup, int, android.content.Context)}. + * This is called by TransitionManager to determine whether it is safe for views from + * this scene to be removed from their parents when the scene is exited, which is + * used by {@link Fade} to fade these views out (the views must be removed from + * their parent in order to add them to the overlay for fading purposes). If a + * Scene is not based on a resource file, then the impact of removing views + * arbitrarily is unknown and should be avoided. + */ + boolean isCreatedFromLayoutResource() { + return (mLayoutId > 0); + } }
\ No newline at end of file diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index a552fd474583..dcf668b6c443 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -118,6 +118,14 @@ public abstract class Transition implements Cloneable { // Scene Root is set at createAnimator() time in the cloned Transition ViewGroup mSceneRoot = null; + // Whether removing views from their parent is possible. This is only for views + // in the start scene, which are no longer in the view hierarchy. This property + // is determined by whether the previous Scene was created from a layout + // resource, and thus the views from the exited scene are going away anyway + // and can be removed as necessary to achieve a particular effect, such as + // removing them from parents to add them to overlays. + boolean mCanRemoveViews = false; + // Track all animators in use in case the transition gets canceled and needs to // cancel running animators private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>(); @@ -1445,6 +1453,10 @@ public abstract class Transition implements Cloneable { return this; } + void setCanRemoveViews(boolean canRemoveViews) { + mCanRemoveViews = canRemoveViews; + } + @Override public String toString() { return toString(""); diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 44ca4e58fd94..9be91d007638 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -178,6 +178,11 @@ public class TransitionManager { Transition transitionClone = transition.clone(); transitionClone.setSceneRoot(sceneRoot); + Scene oldScene = Scene.getCurrentScene(sceneRoot); + if (oldScene != null && oldScene.isCreatedFromLayoutResource()) { + transitionClone.setCanRemoveViews(true); + } + sceneChangeSetup(sceneRoot, transitionClone); scene.enter(); diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 6fdd3093161f..79cd8b693df2 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -340,6 +340,15 @@ public class TransitionSet extends Transition { } @Override + void setCanRemoveViews(boolean canRemoveViews) { + super.setCanRemoveViews(canRemoveViews); + int numTransitions = mTransitions.size(); + for (int i = 0; i < numTransitions; ++i) { + mTransitions.get(i).setCanRemoveViews(canRemoveViews); + } + } + + @Override String toString(String indent) { String result = super.toString(indent); for (int i = 0; i < mTransitions.size(); ++i) { diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index f49821fab1c3..44f92cd7ed4a 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -30,22 +30,6 @@ import android.view.ViewGroup; * changes occur. Subclasses should implement one or both of the methods * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}, * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}, - * - * <p>Note that a view's visibility change is determined by both whether the view - * itself is changing and whether its parent hierarchy's visibility is changing. - * That is, a view that appears in the end scene will only trigger a call to - * {@link #onAppear(android.view.ViewGroup, TransitionValues, int, TransitionValues, int) - * appear()} if its parent hierarchy was stable between the start and end scenes. - * This is done to avoid causing a visibility transition on every node in a hierarchy - * when only the top-most node is the one that should be transitioned in/out. - * Stability is determined by either the parent hierarchy views being the same - * between scenes or, if scenes are inflated from layout resource files and thus - * have result in different view instances, if the views represented by - * the ids of those parents are stable. This means that visibility determination - * is more effective with inflated view hierarchies if ids are used. - * The exception to this is when the visibility subclass transition is - * targeted at specific views, in which case the visibility of parent views - * is ignored.</p> */ public abstract class Visibility extends Transition { @@ -111,51 +95,6 @@ public abstract class Visibility extends Transition { return visibility == View.VISIBLE && parent != null; } - /** - * Tests whether the hierarchy, up to the scene root, changes visibility between - * start and end scenes. This is done to ensure that a view that changes visibility - * is only animated if that view's parent was stable between scenes; we should not - * fade an entire hierarchy, but rather just the top-most node in the hierarchy that - * changed visibility. Note that both the start and end parents are passed in - * because the instances may differ for the same view due to layout inflation - * between scenes. - * - * @param sceneRoot The root of the scene hierarchy - * @param startView The container view in the start scene - * @param endView The container view in the end scene - * @return true if the parent hierarchy experienced a visibility change, false - * otherwise - */ - private boolean isHierarchyVisibilityChanging(ViewGroup sceneRoot, ViewGroup startView, - ViewGroup endView) { - - if (startView == sceneRoot || endView == sceneRoot) { - return false; - } - TransitionValues startValues = startView != null ? - getTransitionValues(startView, true) : getTransitionValues(endView, true); - TransitionValues endValues = endView != null ? - getTransitionValues(endView, false) : getTransitionValues(startView, false); - - if (startValues == null || endValues == null) { - return true; - } - Integer visibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY); - int startVisibility = (visibility != null) ? visibility : -1; - ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT); - visibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY); - int endVisibility = (visibility != null) ? visibility : -1; - ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT); - if (startVisibility != endVisibility || startParent != endParent) { - return true; - } - - if (startParent != null || endParent != null) { - return isHierarchyVisibilityChanging(sceneRoot, startParent, endParent); - } - return false; - } - private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues, TransitionValues endValues) { final VisibilityInfo visInfo = new VisibilityInfo(); @@ -225,9 +164,7 @@ public abstract class Visibility extends Transition { int endId = endView != null ? endView.getId() : -1; isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId); } - if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null) && - !isHierarchyVisibilityChanging(sceneRoot, - visInfo.startParent, visInfo.endParent))) { + if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) { if (visInfo.fadeIn) { return onAppear(sceneRoot, startValues, visInfo.startVisibility, endValues, visInfo.endVisibility); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index aea2799c2835..caf9c8b810ca 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -249,12 +249,6 @@ interface IWindowManager boolean isSafeModeEnabled(); /** - * Tell keyguard to show the assistant (Intent.ACTION_ASSIST) after asking for the user's - * credentials. - */ - void showAssistant(); - - /** * Sets the display magnification callbacks. These callbacks notify * the client for contextual changes related to display magnification. * diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 38f28ae20354..28f7480300ca 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4825,8 +4825,8 @@ public final class ViewRootImpl implements ViewParent, enqueueInputEvent(new KeyEvent(event.getDownTime(), event.getEventTime(), event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(), - event.getScanCode(), event.getFlags() | KeyEvent.FLAG_FALLBACK, - event.getSource())); + event.getDeviceId(), event.getScanCode(), + event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource())); return true; } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index e11666217505..79c0b3c0db7e 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1176,12 +1176,6 @@ public interface WindowManagerPolicy { public void dump(String prefix, PrintWriter writer, String[] args); /** - * Ask keyguard to invoke the assist intent after dismissing keyguard - * {@link android.content.Intent#ACTION_ASSIST} - */ - public void showAssistant(); - - /** * Returns whether a given window type can be magnified. * * @param windowType The window type. diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index e7d84c2e6c91..59330caf0e41 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -337,14 +337,17 @@ public interface InputConnection { public boolean deleteSurroundingText(int beforeLength, int afterLength); /** - * Set composing text around the current cursor position with the - * given text, and set the new cursor position. Any composing text - * set previously will be removed automatically. + * Replace the currently composing text with the given text, and + * set the new cursor position. Any composing text set previously + * will be removed automatically. * * <p>If there is any composing span currently active, all * characters that it comprises are removed. The passed text is * added in its place, and a composing span is added to this - * text. Finally, the cursor is moved to the location specified by + * text. If there is no composing span active, the passed text is + * added at the cursor position (removing selected characters + * first if any), and a composing span is added on the new text. + * Finally, the cursor is moved to the location specified by * <code>newCursorPosition</code>.</p> * * <p>This is usually called by IMEs to add or remove or change @@ -447,8 +450,10 @@ public interface InputConnection { * * <p>This method removes the contents of the currently composing * text and replaces it with the passed CharSequence, and then - * moves the cursor according to {@code newCursorPosition}. - * This behaves like calling + * moves the cursor according to {@code newCursorPosition}. If there + * is no composing text when this method is called, the new text is + * inserted at the cursor position, removing text inside the selection + * if any. This behaves like calling * {@link #setComposingText(CharSequence, int) setComposingText(text, newCursorPosition)} * then {@link #finishComposingText()}.</p> * @@ -461,15 +466,16 @@ public interface InputConnection { * but be careful to wait until the batch edit is over if one is * in progress.</p> * - * @param text The committed text. This may include styles. - * @param newCursorPosition The new cursor position around the text. If - * > 0, this is relative to the end of the text - 1; if <= 0, this - * is relative to the start of the text. So a value of 1 will - * always advance you to the position after the full text being - * inserted. Note that this means you can't position the cursor - * within the text, because the editor can make modifications to - * the text you are providing so it is not possible to correctly - * specify locations there. + * @param text The text to commit. This may include styles. + * @param newCursorPosition The new cursor position around the text, + * in Java characters. If > 0, this is relative to the end + * of the text - 1; if <= 0, this is relative to the start + * of the text. So a value of 1 will always advance the cursor + * to the position after the full text being inserted. Note that + * this means you can't position the cursor within the text, + * because the editor can make modifications to the text + * you are providing so it is not possible to correctly specify + * locations there. * @return true on success, false if the input connection is no longer * valid. */ diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java index 6c4c39d9456f..d4c5be0615a7 100644 --- a/core/java/android/widget/CursorAdapter.java +++ b/core/java/android/widget/CursorAdapter.java @@ -26,9 +26,13 @@ import android.view.View; import android.view.ViewGroup; /** - * Adapter that exposes data from a {@link android.database.Cursor Cursor} to a - * {@link android.widget.ListView ListView} widget. The Cursor must include - * a column named "_id" or this class will not work. + * Adapter that exposes data from a {@link android.database.Cursor Cursor} to a + * {@link android.widget.ListView ListView} widget. + * <p> + * The Cursor must include a column named "_id" or this class will not work. + * Additionally, using {@link android.database.MergeCursor} with this class will + * not work if the merged Cursors have overlapping values in their "_id" + * columns. */ public abstract class CursorAdapter extends BaseAdapter implements Filterable, CursorFilter.CursorFilterClient { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index b7e1fddc4e34..66fe46f29fce 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.IntProperty; import android.util.Log; +import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -76,6 +77,8 @@ public class ListPopupWindow { private int mDropDownVerticalOffset; private boolean mDropDownVerticalOffsetSet; + private int mDropDownGravity = Gravity.NO_GRAVITY; + private boolean mDropDownAlwaysVisible = false; private boolean mForceIgnoreOutsideTouch = false; int mListItemExpandMaximum = Integer.MAX_VALUE; @@ -439,6 +442,16 @@ public class ListPopupWindow { } /** + * Set the gravity of the dropdown list. This is commonly used to + * set gravity to START or END for alignment with the anchor. + * + * @param gravity Gravity value to use + */ + public void setDropDownGravity(int gravity) { + mDropDownGravity = gravity; + } + + /** * @return The width of the popup window in pixels. */ public int getWidth() { @@ -610,7 +623,7 @@ public class ListPopupWindow { mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible); mPopup.setTouchInterceptor(mTouchInterceptor); mPopup.showAsDropDown(getAnchorView(), - mDropDownHorizontalOffset, mDropDownVerticalOffset); + mDropDownHorizontalOffset, mDropDownVerticalOffset, mDropDownGravity); mDropDownList.setSelection(ListView.INVALID_POSITION); if (!mModal || mDropDownList.isInTouchMode()) { diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java index 9ac6a5969dea..111dadc5f868 100644 --- a/core/java/android/widget/PopupMenu.java +++ b/core/java/android/widget/PopupMenu.java @@ -22,6 +22,7 @@ import com.android.internal.view.menu.MenuPresenter; import com.android.internal.view.menu.SubMenuBuilder; import android.content.Context; +import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -64,12 +65,25 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback { * is room, or above it if there is not. */ public PopupMenu(Context context, View anchor) { + this(context, anchor, Gravity.NO_GRAVITY); + } + + /** + * Construct a new PopupMenu. + * + * @param context Context for the PopupMenu. + * @param anchor Anchor view for this popup. The popup will appear below the anchor if there + * is room, or above it if there is not. + * @param gravity The {@link Gravity} value for aligning the popup with its anchor + */ + public PopupMenu(Context context, View anchor, int gravity) { // TODO Theme? mContext = context; mMenu = new MenuBuilder(context); mMenu.setCallback(this); mAnchor = anchor; mPopup = new MenuPopupHelper(context, mMenu, anchor); + mPopup.setGravity(gravity); mPopup.setCallback(this); } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 14607376a932..5663959bdfe3 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -72,7 +72,9 @@ public class PopupWindow { * screen as needed, regardless of whether this covers the input method. */ public static final int INPUT_METHOD_NOT_NEEDED = 2; - + + private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START; + private Context mContext; private WindowManager mWindowManager; @@ -135,12 +137,13 @@ public class PopupWindow { WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); - updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff)); + updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff, + mAnchoredGravity)); update(p.x, p.y, -1, -1, true); } } }; - private int mAnchorXoff, mAnchorYoff; + private int mAnchorXoff, mAnchorYoff, mAnchoredGravity; private boolean mPopupViewInitialLayoutDirectionInherited; @@ -873,15 +876,38 @@ public class PopupWindow { * location, the popup will be moved correspondingly.</p> * * @param anchor the view on which to pin the popup window + * @param xoff A horizontal offset from the anchor in pixels + * @param yoff A vertical offset from the anchor in pixels * * @see #dismiss() */ public void showAsDropDown(View anchor, int xoff, int yoff) { + showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY); + } + + /** + * <p>Display the content view in a popup window anchored to the bottom-left + * corner of the anchor view offset by the specified x and y coordinates. + * If there is not enough room on screen to show + * the popup in its entirety, this method tries to find a parent scroll + * view to scroll. If no parent scroll view can be scrolled, the bottom-left + * corner of the popup is pinned at the top left corner of the anchor view.</p> + * <p>If the view later scrolls to move <code>anchor</code> to a different + * location, the popup will be moved correspondingly.</p> + * + * @param anchor the view on which to pin the popup window + * @param xoff A horizontal offset from the anchor in pixels + * @param yoff A vertical offset from the anchor in pixels + * @param gravity Alignment of the popup relative to the anchor + * + * @see #dismiss() + */ + public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { if (isShowing() || mContentView == null) { return; } - registerForScrollChanged(anchor, xoff, yoff); + registerForScrollChanged(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; @@ -889,7 +915,7 @@ public class PopupWindow { WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken()); preparePopup(p); - updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff)); + updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity)); if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; if (mWidthMode < 0) p.width = mLastWidth = mWidthMode; @@ -1105,17 +1131,24 @@ public class PopupWindow { * @return true if the popup is translated upwards to fit on screen */ private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, - int xoff, int yoff) { + int xoff, int yoff, int gravity) { final int anchorHeight = anchor.getHeight(); anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchorHeight + yoff; + + final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection()) & + Gravity.HORIZONTAL_GRAVITY_MASK; + if (hgrav == Gravity.RIGHT) { + // Flip the location to align the right sides of the popup and anchor instead of left + p.x -= mPopupWidth - anchor.getWidth(); + } boolean onTop = false; - p.gravity = Gravity.START | Gravity.TOP; - + p.gravity = Gravity.LEFT | Gravity.TOP; + anchor.getLocationOnScreen(mScreenLocation); final Rect displayFrame = new Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); @@ -1141,6 +1174,11 @@ public class PopupWindow { anchor.getLocationInWindow(mDrawingLocation); p.x = mDrawingLocation[0] + xoff; p.y = mDrawingLocation[1] + anchor.getHeight() + yoff; + + // Preserve the gravity adjustment + if (hgrav == Gravity.RIGHT) { + p.x -= mPopupWidth - anchor.getWidth(); + } // determine whether there is more space above or below the anchor anchor.getLocationOnScreen(mScreenLocation); @@ -1148,7 +1186,7 @@ public class PopupWindow { onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getHeight() - yoff) < (mScreenLocation[1] - yoff - displayFrame.top); if (onTop) { - p.gravity = Gravity.START | Gravity.BOTTOM; + p.gravity = Gravity.LEFT | Gravity.BOTTOM; p.y = root.getHeight() - mDrawingLocation[1] + yoff; } else { p.y = mDrawingLocation[1] + anchor.getHeight() + yoff; @@ -1436,7 +1474,7 @@ public class PopupWindow { * @param height the new height, can be -1 to ignore */ public void update(View anchor, int width, int height) { - update(anchor, false, 0, 0, true, width, height); + update(anchor, false, 0, 0, true, width, height, mAnchoredGravity); } /** @@ -1455,11 +1493,11 @@ public class PopupWindow { * @param height the new height, can be -1 to ignore */ public void update(View anchor, int xoff, int yoff, int width, int height) { - update(anchor, true, xoff, yoff, true, width, height); + update(anchor, true, xoff, yoff, true, width, height, mAnchoredGravity); } private void update(View anchor, boolean updateLocation, int xoff, int yoff, - boolean updateDimension, int width, int height) { + boolean updateDimension, int width, int height, int gravity) { if (!isShowing() || mContentView == null) { return; @@ -1468,11 +1506,12 @@ public class PopupWindow { WeakReference<View> oldAnchor = mAnchor; final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff); if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) { - registerForScrollChanged(anchor, xoff, yoff); + registerForScrollChanged(anchor, xoff, yoff, gravity); } else if (needsUpdate) { // No need to register again if this is a DropDown, showAsDropDown already did. mAnchorXoff = xoff; mAnchorYoff = yoff; + mAnchoredGravity = gravity; } WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); @@ -1494,9 +1533,10 @@ public class PopupWindow { int y = p.y; if (updateLocation) { - updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff)); + updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, gravity)); } else { - updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff)); + updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff, + mAnchoredGravity)); } update(p.x, p.y, width, height, x != p.x || y != p.y); @@ -1525,7 +1565,7 @@ public class PopupWindow { mAnchor = null; } - private void registerForScrollChanged(View anchor, int xoff, int yoff) { + private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) { unregisterForScrollChanged(); mAnchor = new WeakReference<View>(anchor); @@ -1536,6 +1576,7 @@ public class PopupWindow { mAnchorXoff = xoff; mAnchorYoff = yoff; + mAnchoredGravity = gravity; } private class PopupViewContainer extends FrameLayout { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ae1b6279609d..61e071b8673b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4679,8 +4679,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener assumeLayout(); } - boolean changed = false; - if (mMovement != null) { /* This code also provides auto-scrolling when a cursor is moved using a * CursorController (insertion point or selection limits). @@ -4703,10 +4701,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (curs >= 0) { - changed = bringPointIntoView(curs); + bringPointIntoView(curs); } } else { - changed = bringTextIntoView(); + bringTextIntoView(); } // This has to be checked here since: @@ -4727,7 +4725,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener getViewTreeObserver().removeOnPreDrawListener(this); mPreDrawRegistered = false; - return !changed; + return true; } @Override @@ -7838,7 +7836,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener getCompoundPaddingLeft() - getCompoundPaddingRight() - mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength(); case Gravity.CENTER_HORIZONTAL: - return 0.0f; + case Gravity.FILL_HORIZONTAL: + final int textDirection = mLayout.getParagraphDirection(0); + if (textDirection == Layout.DIR_LEFT_TO_RIGHT) { + return 0.0f; + } else { + return (mLayout.getLineRight(0) - (mRight - mLeft) - + getCompoundPaddingLeft() - getCompoundPaddingRight() - + mLayout.getLineLeft(0)) / getHorizontalFadingEdgeLength(); + } } } } @@ -7867,9 +7873,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return 0.0f; case Gravity.CENTER_HORIZONTAL: case Gravity.FILL_HORIZONTAL: - return (mLayout.getLineWidth(0) - ((mRight - mLeft) - + final int textDirection = mLayout.getParagraphDirection(0); + if (textDirection == Layout.DIR_RIGHT_TO_LEFT) { + return 0.0f; + } else { + return (mLayout.getLineWidth(0) - ((mRight - mLeft) - getCompoundPaddingLeft() - getCompoundPaddingRight())) / getHorizontalFadingEdgeLength(); + } } } } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 009b729ffc75..fbdf318752af 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -33,6 +33,7 @@ import android.media.SubtitleController; import android.media.SubtitleTrack.RenderingWidget; import android.media.WebVttRenderer; import android.net.Uri; +import android.os.Looper; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -879,4 +880,10 @@ public class VideoView extends SurfaceView invalidate(); } + + /** @hide */ + @Override + public Looper getSubtitleLooper() { + return Looper.getMainLooper(); + } } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index a281f7c2ee0e..a95bac818c8b 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -47,6 +47,10 @@ public final class ProcessStats implements Parcelable { public static final String SERVICE_NAME = "procstats"; + // How often the service commits its data, giving the minimum batching + // that is done. + public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours + public static final int STATE_NOTHING = -1; public static final int STATE_PERSISTENT = 0; public static final int STATE_TOP = 1; @@ -1015,7 +1019,7 @@ public final class ProcessStats implements Parcelable { pkgMap.removeAt(ip); } } - mStartTime = SystemClock.uptimeMillis(); + mStartTime = now; if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); } @@ -1449,7 +1453,7 @@ public final class ProcessStats implements Parcelable { mReadError = "bad uid: " + uid; return; } - PackageState pkgState = new PackageState(uid); + PackageState pkgState = new PackageState(pkgName, uid); mPackages.put(pkgName, uid, pkgState); int NPROCS = in.readInt(); if (NPROCS < 0) { @@ -1620,7 +1624,7 @@ public final class ProcessStats implements Parcelable { if (as != null) { return as; } - as = new PackageState(uid); + as = new PackageState(packageName, uid); mPackages.put(packageName, uid, as); return as; } @@ -1646,11 +1650,22 @@ public final class ProcessStats implements Parcelable { // but it was created for a different package than the caller. // We need to convert it to a multi-package process. commonProc.mMultiPackage = true; - // The original package it was created for now needs to point - // to its own copy. + // To do this, we need to make two new process states, one a copy + // of the current state for the process under the original package + // name, and the second a free new process state for it as the + // new package name. long now = SystemClock.uptimeMillis(); - pkgState.mProcesses.put(commonProc.mName, commonProc.clone( - commonProc.mPackage, now)); + // First let's make a copy of the current process state and put + // that under the now unique state for its original package name. + final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage, uid); + if (commonPkgState != null) { + commonPkgState.mProcesses.put(commonProc.mName, commonProc.clone( + commonProc.mPackage, now)); + } else { + Slog.w(TAG, "Cloning proc state: no package state " + commonProc.mPackage + + "/" + uid + " for proc " + commonProc.mName); + } + // And now make a fresh new process state for the new package name. ps = new ProcessState(commonProc, packageName, uid, processName, now); } } else { @@ -1677,6 +1692,32 @@ public final class ProcessStats implements Parcelable { return ss; } + private void dumpProcessInternalLocked(PrintWriter pw, String prefix, ProcessState proc, + boolean dumpAll) { + if (dumpAll) { + pw.print(prefix); pw.print("myID="); + pw.print(Integer.toHexString(System.identityHashCode(proc))); + pw.print(" mCommonProcess="); + pw.print(Integer.toHexString(System.identityHashCode(proc.mCommonProcess))); + pw.print(" mPackage="); pw.println(proc.mPackage); + if (proc.mMultiPackage) { + pw.print(prefix); pw.print("mMultiPackage="); pw.println(proc.mMultiPackage); + } + if (proc != proc.mCommonProcess) { + pw.print(prefix); pw.print("Common Proc: "); pw.print(proc.mCommonProcess.mName); + pw.print("/"); pw.print(proc.mCommonProcess.mUid); + pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage); + } + } + pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive); + if (proc.mDead) { + pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead); + } + pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices); + pw.print(" mNumStartedServices="); + pw.println(proc.mNumStartedServices); + } + public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, boolean dumpAll) { long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, @@ -1715,10 +1756,7 @@ public final class ProcessStats implements Parcelable { ALL_PROC_STATES, now); dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES); - pw.print(" mActive="); pw.println(proc.mActive); - pw.print(" mNumActiveServices="); pw.print(proc.mNumActiveServices); - pw.print(" mNumStartedServices="); - pw.println(proc.mNumStartedServices); + dumpProcessInternalLocked(pw, " ", proc, dumpAll); } } else { ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); @@ -1737,6 +1775,7 @@ public final class ProcessStats implements Parcelable { pw.print(pkgState.mServices.keyAt(isvc)); pw.println(":"); ServiceState svc = pkgState.mServices.valueAt(isvc); + pw.print(" Process: "); pw.println(svc.mProcessName); dumpServiceStats(pw, " ", " ", " ", "Running", svc, svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState, svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll); @@ -1759,16 +1798,19 @@ public final class ProcessStats implements Parcelable { if (reqPackage == null) { ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); printedHeader = false; + int numShownProcs = 0, numTotalProcs = 0; for (int ip=0; ip<procMap.size(); ip++) { String procName = procMap.keyAt(ip); SparseArray<ProcessState> uids = procMap.valueAt(ip); for (int iu=0; iu<uids.size(); iu++) { int uid = uids.keyAt(iu); + numTotalProcs++; ProcessState proc = uids.valueAt(iu); if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING && proc.mPssTableSize == 0) { continue; } + numShownProcs++; if (!printedHeader) { pw.println(); pw.println("Per-Process Stats:"); @@ -1783,13 +1825,15 @@ public final class ProcessStats implements Parcelable { dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES); if (dumpAll) { - pw.print(" mActive="); pw.println(proc.mActive); - pw.print(" mNumActiveServices="); pw.print(proc.mNumActiveServices); - pw.print(" mNumStartedServices="); - pw.println(proc.mNumStartedServices); + dumpProcessInternalLocked(pw, " ", proc, dumpAll); } } } + if (dumpAll) { + pw.println(); + pw.print(" Total procs: "); pw.print(numShownProcs); + pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); + } pw.println(); if (dumpSummary) { @@ -1876,7 +1920,7 @@ public final class ProcessStats implements Parcelable { long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, mStartTime, now); dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - NON_CACHED_PROC_STATES, now, totalTime, reqPackage); + ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage); pw.println(); dumpTotalsLocked(pw, now); } @@ -1916,22 +1960,22 @@ public final class ProcessStats implements Parcelable { } void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, - int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime, - String reqPackage) { + int[] screenStates, int[] memStates, int[] procStates, + int[] sortProcStates, long now, long totalTime, String reqPackage) { ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, - procStates, now, reqPackage); + procStates, sortProcStates, now, reqPackage); if (procs.size() > 0) { if (header != null) { pw.println(); pw.println(header); } - dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates, - now, totalTime); + dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, + sortProcStates, now, totalTime); } } public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, - int[] procStates, long now, String reqPackage) { + int[] procStates, int sortProcStates[], long now, String reqPackage) { ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); for (int ip=0; ip<pkgMap.size(); ip++) { @@ -1953,6 +1997,9 @@ public final class ProcessStats implements Parcelable { if (computeProcessTimeLocked(proc, screenStates, memStates, procStates, now) > 0) { outProcs.add(proc); + if (procStates != sortProcStates) { + computeProcessTimeLocked(proc, screenStates, memStates, sortProcStates, now); + } } } Collections.sort(outProcs, new Comparator<ProcessState>() { @@ -2134,15 +2181,97 @@ public final class ProcessStats implements Parcelable { pw.println(); } - public static final class ProcessState { + public static class DurationsTable { public final ProcessStats mStats; + public final String mName; + public int[] mDurationsTable; + public int mDurationsTableSize; + + public DurationsTable(ProcessStats stats, String name) { + mStats = stats; + mName = name; + } + + void copyDurationsTo(DurationsTable other) { + if (mDurationsTable != null) { + mStats.mAddLongTable = new int[mDurationsTable.length]; + mStats.mAddLongTableSize = 0; + for (int i=0; i<mDurationsTableSize; i++) { + int origEnt = mDurationsTable[i]; + int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + int newOff = mStats.addLongData(i, type, 1); + mStats.mAddLongTable[i] = newOff | type; + mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0)); + } + other.mDurationsTable = mStats.mAddLongTable; + other.mDurationsTableSize = mStats.mAddLongTableSize; + } else { + other.mDurationsTable = null; + other.mDurationsTableSize = 0; + } + } + + void addDurations(DurationsTable other) { + for (int i=0; i<other.mDurationsTableSize; i++) { + int ent = other.mDurationsTable[i]; + int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; + if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration " + + other.mStats.getLong(ent, 0)); + addDuration(state, other.mStats.getLong(ent, 0)); + } + } + + void resetDurationsSafely() { + mDurationsTable = null; + mDurationsTableSize = 0; + } + + void writeDurationsToParcel(Parcel out) { + out.writeInt(mDurationsTableSize); + for (int i=0; i<mDurationsTableSize; i++) { + if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " + + printLongOffset(mDurationsTable[i])); + out.writeInt(mDurationsTable[i]); + } + } + + boolean readDurationsFromParcel(Parcel in) { + mDurationsTable = mStats.readTableFromParcel(in, mName, "durations"); + if (mDurationsTable == BAD_TABLE) { + return false; + } + mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; + return true; + } + + void addDuration(int state, long dur) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + int off; + if (idx >= 0) { + off = mDurationsTable[idx]; + } else { + mStats.mAddLongTable = mDurationsTable; + mStats.mAddLongTableSize = mDurationsTableSize; + off = mStats.addLongData(~idx, state, 1); + mDurationsTable = mStats.mAddLongTable; + mDurationsTableSize = mStats.mAddLongTableSize; + } + long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); + if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur + + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]); + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; + } + + long getDuration(int state, long now) { + int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); + return idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; + } + } + + public static final class ProcessState extends DurationsTable { public final ProcessState mCommonProcess; public final String mPackage; public final int mUid; - public final String mName; - - int[] mDurationsTable; - int mDurationsTableSize; //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT]; int mCurState = STATE_NOTHING; @@ -2175,11 +2304,10 @@ public final class ProcessStats implements Parcelable { * a single package running in a process. The initial state is not running. */ public ProcessState(ProcessStats processStats, String pkg, int uid, String name) { - mStats = processStats; + super(processStats, name); mCommonProcess = this; mPackage = pkg; mUid = uid; - mName = name; } /** @@ -2189,30 +2317,17 @@ public final class ProcessStats implements Parcelable { */ public ProcessState(ProcessState commonProcess, String pkg, int uid, String name, long now) { - mStats = commonProcess.mStats; + super(commonProcess.mStats, name); mCommonProcess = commonProcess; mPackage = pkg; mUid = uid; - mName = name; mCurState = commonProcess.mCurState; mStartTime = now; } ProcessState clone(String pkg, long now) { ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now); - if (mDurationsTable != null) { - mStats.mAddLongTable = new int[mDurationsTable.length]; - mStats.mAddLongTableSize = 0; - for (int i=0; i<mDurationsTableSize; i++) { - int origEnt = mDurationsTable[i]; - int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int newOff = mStats.addLongData(i, type, 1); - mStats.mAddLongTable[i] = newOff | type; - mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0)); - } - pnew.mDurationsTable = mStats.mAddLongTable; - pnew.mDurationsTableSize = mStats.mAddLongTableSize; - } + copyDurationsTo(pnew); if (mPssTable != null) { mStats.mAddLongTable = new int[mPssTable.length]; mStats.mAddLongTableSize = 0; @@ -2240,13 +2355,7 @@ public final class ProcessStats implements Parcelable { } void add(ProcessState other) { - for (int i=0; i<other.mDurationsTableSize; i++) { - int ent = other.mDurationsTable[i]; - int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration " - + other.mStats.getLong(ent, 0)); - addDuration(state, other.mStats.getLong(ent, 0)); - } + addDurations(other); for (int i=0; i<other.mPssTableSize; i++) { int ent = other.mPssTable[i]; int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; @@ -2267,8 +2376,7 @@ public final class ProcessStats implements Parcelable { } void resetSafely(long now) { - mDurationsTable = null; - mDurationsTableSize = 0; + resetDurationsSafely(); mStartTime = now; mLastPssState = STATE_NOTHING; mLastPssTime = 0; @@ -2294,12 +2402,7 @@ public final class ProcessStats implements Parcelable { void writeToParcel(Parcel out, long now) { out.writeInt(mMultiPackage ? 1 : 0); - out.writeInt(mDurationsTableSize); - for (int i=0; i<mDurationsTableSize; i++) { - if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " - + printLongOffset(mDurationsTable[i])); - out.writeInt(mDurationsTable[i]); - } + writeDurationsToParcel(out); out.writeInt(mPssTableSize); for (int i=0; i<mPssTableSize; i++) { if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": " @@ -2322,11 +2425,9 @@ public final class ProcessStats implements Parcelable { mMultiPackage = multiPackage; } if (DEBUG) Slog.d(TAG, "Reading durations table..."); - mDurationsTable = mStats.readTableFromParcel(in, mName, "durations"); - if (mDurationsTable == BAD_TABLE) { + if (!readDurationsFromParcel(in)) { return false; } - mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; if (DEBUG) Slog.d(TAG, "Reading pss table..."); mPssTable = mStats.readTableFromParcel(in, mName, "pss"); if (mPssTable == BAD_TABLE) { @@ -2411,24 +2512,6 @@ public final class ProcessStats implements Parcelable { mStartTime = now; } - void addDuration(int state, long dur) { - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - int off; - if (idx >= 0) { - off = mDurationsTable[idx]; - } else { - mStats.mAddLongTable = mDurationsTable; - mStats.mAddLongTableSize = mDurationsTableSize; - off = mStats.addLongData(~idx, state, 1); - mDurationsTable = mStats.mAddLongTable; - mDurationsTableSize = mStats.mAddLongTableSize; - } - long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur - + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]); - longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; - } - void incActiveServices() { if (mCommonProcess != this) { mCommonProcess.incActiveServices(); @@ -2470,7 +2553,8 @@ public final class ProcessStats implements Parcelable { } } - public void addPss(long pss, long uss, boolean always) { + public void addPss(long pss, long uss, boolean always, + ArrayMap<String, ProcessState> pkgList) { ensureNotDead(); if (!always) { if (mLastPssState == mCurState && SystemClock.uptimeMillis() @@ -2481,7 +2565,20 @@ public final class ProcessStats implements Parcelable { mLastPssState = mCurState; mLastPssTime = SystemClock.uptimeMillis(); if (mCurState != STATE_NOTHING) { - addPss(mCurState, 1, pss, pss, pss, uss, uss, uss); + // First update the common process. + mCommonProcess.addPss(mCurState, 1, pss, pss, pss, uss, uss, uss); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + return; + } + + if (pkgList != null) { + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).addPss(mCurState, 1, + pss, pss, pss, uss, uss, uss); + } + } } } @@ -2632,8 +2729,7 @@ public final class ProcessStats implements Parcelable { } long getDuration(int state, long now) { - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; + long time = super.getDuration(state, now); if (mCurState == state) { time += now - mStartTime; } @@ -2676,10 +2772,8 @@ public final class ProcessStats implements Parcelable { } } - public static final class ServiceState { - final ProcessStats mStats; + public static final class ServiceState extends DurationsTable { public final String mPackage; - public final String mName; public final String mProcessName; ProcessState mProc; @@ -2691,9 +2785,6 @@ public final class ProcessStats implements Parcelable { public static final int SERVICE_EXEC = 3; static final int SERVICE_COUNT = 4; - int[] mDurationsTable; - int mDurationsTableSize; - int mRunCount; public int mRunState = STATE_NOTHING; long mRunStartTime; @@ -2712,9 +2803,8 @@ public final class ProcessStats implements Parcelable { public ServiceState(ProcessStats processStats, String pkg, String name, String processName, ProcessState proc) { - mStats = processStats; + super(processStats, name); mPackage = pkg; - mName = name; mProcessName = processName; mProc = proc; } @@ -2743,11 +2833,7 @@ public final class ProcessStats implements Parcelable { } void add(ServiceState other) { - for (int i=0; i<other.mDurationsTableSize; i++) { - int ent = other.mDurationsTable[i]; - int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - addStateTime(state, other.mStats.getLong(ent, 0)); - } + addDurations(other); mRunCount += other.mRunCount; mStartedCount += other.mStartedCount; mBoundCount += other.mBoundCount; @@ -2755,22 +2841,16 @@ public final class ProcessStats implements Parcelable { } void resetSafely(long now) { - mDurationsTable = null; - mDurationsTableSize = 0; + resetDurationsSafely(); mRunCount = mRunState != STATE_NOTHING ? 1 : 0; mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; mExecCount = mExecState != STATE_NOTHING ? 1 : 0; - mStartedStartTime = mBoundStartTime = mExecStartTime = now; + mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now; } void writeToParcel(Parcel out, long now) { - out.writeInt(mDurationsTableSize); - for (int i=0; i<mDurationsTableSize; i++) { - if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": " - + printLongOffset(mDurationsTable[i])); - out.writeInt(mDurationsTable[i]); - } + writeDurationsToParcel(out); out.writeInt(mRunCount); out.writeInt(mStartedCount); out.writeInt(mBoundCount); @@ -2778,12 +2858,9 @@ public final class ProcessStats implements Parcelable { } boolean readFromParcel(Parcel in) { - if (DEBUG) Slog.d(TAG, "Reading durations table..."); - mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service"); - if (mDurationsTable == BAD_TABLE) { + if (!readDurationsFromParcel(in)) { return false; } - mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; mRunCount = in.readInt(); mStartedCount = in.readInt(); mBoundCount = in.readInt(); @@ -2791,40 +2868,22 @@ public final class ProcessStats implements Parcelable { return true; } - void addStateTime(int state, long time) { - if (time > 0) { - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - int off; - if (idx >= 0) { - off = mDurationsTable[idx]; - } else { - mStats.mAddLongTable = mDurationsTable; - mStats.mAddLongTableSize = mDurationsTableSize; - off = mStats.addLongData(~idx, state, 1); - mDurationsTable = mStats.mAddLongTable; - mDurationsTableSize = mStats.mAddLongTableSize; - } - long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time; - } - } - void commitStateTime(long now) { if (mRunState != STATE_NOTHING) { - addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime); + addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime); mRunStartTime = now; } if (mStartedState != STATE_NOTHING) { - addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), now - mStartedStartTime); mStartedStartTime = now; } if (mBoundState != STATE_NOTHING) { - addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime); + addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime); mBoundStartTime = now; } if (mExecState != STATE_NOTHING) { - addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); + addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); mExecStartTime = now; } } @@ -2834,7 +2893,7 @@ public final class ProcessStats implements Parcelable { || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING; if (mRunState != state) { if (mRunState != STATE_NOTHING) { - addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT), + addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime); } else if (state != STATE_NOTHING) { mRunCount++; @@ -2852,7 +2911,7 @@ public final class ProcessStats implements Parcelable { final int state = started ? memFactor : STATE_NOTHING; if (mStartedState != state) { if (mStartedState != STATE_NOTHING) { - addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), now - mStartedStartTime); } else if (started) { mStartedCount++; @@ -2878,7 +2937,7 @@ public final class ProcessStats implements Parcelable { final int state = bound ? memFactor : STATE_NOTHING; if (mBoundState != state) { if (mBoundState != STATE_NOTHING) { - addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), + addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime); } else if (bound) { mBoundCount++; @@ -2896,7 +2955,7 @@ public final class ProcessStats implements Parcelable { final int state = executing ? memFactor : STATE_NOTHING; if (mExecState != state) { if (mExecState != STATE_NOTHING) { - addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); + addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); } else if (executing) { mExecCount++; } @@ -2909,8 +2968,7 @@ public final class ProcessStats implements Parcelable { private long getDuration(int opType, int curState, long startTime, int memFactor, long now) { int state = opType + (memFactor*SERVICE_COUNT); - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; + long time = getDuration(state, now); if (curState == memFactor) { time += now - startTime; } @@ -2923,10 +2981,12 @@ public final class ProcessStats implements Parcelable { = new ArrayMap<String, ProcessState>(); public final ArrayMap<String, ServiceState> mServices = new ArrayMap<String, ServiceState>(); - final int mUid; + public final String mPackageName; + public final int mUid; - public PackageState(int uid) { + public PackageState(String packageName, int uid) { mUid = uid; + mPackageName = packageName; } } @@ -2951,6 +3011,9 @@ public final class ProcessStats implements Parcelable { } void print(PrintWriter pw, long overallTime, boolean full) { + if (totalTime > overallTime) { + pw.print("*"); + } printPercent(pw, (double) totalTime / (double) overallTime); if (numPss > 0) { pw.print(" ("); diff --git a/core/java/com/android/internal/transition/ActionBarTransition.java b/core/java/com/android/internal/transition/ActionBarTransition.java index 8beae8cfa586..c1065e7e971d 100644 --- a/core/java/com/android/internal/transition/ActionBarTransition.java +++ b/core/java/com/android/internal/transition/ActionBarTransition.java @@ -19,7 +19,7 @@ package com.android.internal.transition; import android.transition.ChangeBounds; import android.transition.Fade; -import android.transition.TextChange; +import android.transition.ChangeText; import android.transition.Transition; import android.transition.TransitionManager; import android.transition.TransitionSet; @@ -35,8 +35,8 @@ public class ActionBarTransition { static { if (TRANSITIONS_ENABLED) { - final TextChange tc = new TextChange(); - tc.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT_IN); + final ChangeText tc = new ChangeText(); + tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); final TransitionSet inner = new TransitionSet(); inner.addTransition(tc).addTransition(new ChangeBounds()); final TransitionSet tg = new TransitionSet(); diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 4c6ddbf90cb4..6471e145722f 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -25,6 +25,7 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.util.SparseBooleanArray; import android.view.ActionProvider; +import android.view.Gravity; import android.view.MenuItem; import android.view.SoundEffectConstants; import android.view.View; @@ -665,6 +666,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter public OverflowPopup(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly) { super(context, menu, anchorView, overflowOnly); + setGravity(Gravity.END); setCallback(mPopupPresenterCallback); } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index dbb78c28675b..05e9a66ce5ed 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -19,6 +19,7 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Resources; import android.os.Parcelable; +import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; @@ -69,6 +70,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On /** Cached content width from {@link #measureContentWidth}. */ private int mContentWidth; + private int mDropDownGravity = Gravity.NO_GRAVITY; + public MenuPopupHelper(Context context, MenuBuilder menu) { this(context, menu, null, false); } @@ -102,6 +105,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On mForceShowIcon = forceShow; } + public void setGravity(int gravity) { + mDropDownGravity = gravity; + } + public void show() { if (!tryShow()) { throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); @@ -126,6 +133,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this); anchor.addOnAttachStateChangeListener(this); mPopup.setAnchorView(anchor); + mPopup.setDropDownGravity(mDropDownGravity); } else { return false; } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index a6566d5c9628..b5d74e857807 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -29,12 +29,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.Layout; import android.text.TextUtils; -import android.transition.ChangeBounds; -import android.transition.Fade; -import android.transition.TextChange; -import android.transition.Transition; -import android.transition.TransitionManager; -import android.transition.TransitionSet; import android.util.AttributeSet; import android.view.CollapsibleActionView; import android.view.Gravity; diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 15792e8b345d..62f057ff1bcb 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -156,31 +156,32 @@ struct graphics_memory_pss * Uses libmemtrack to retrieve graphics memory that the process is using. * Any graphics memory reported in /proc/pid/smaps is not included here. */ -static int read_memtrack_memory(struct memtrack_proc* p, int pid, struct graphics_memory_pss* graphics_mem) +static int read_memtrack_memory(struct memtrack_proc* p, int pid, + struct graphics_memory_pss* graphics_mem) { int err = memtrack_proc_get(p, pid); if (err != 0) { - ALOGE("failed to get memory consumption info: %d", err); + ALOGW("failed to get memory consumption info: %d", err); return err; } ssize_t pss = memtrack_proc_graphics_pss(p); if (pss < 0) { - ALOGE("failed to get graphics pss: %d", pss); + ALOGW("failed to get graphics pss: %d", pss); return pss; } graphics_mem->graphics = pss / 1024; pss = memtrack_proc_gl_pss(p); if (pss < 0) { - ALOGE("failed to get gl pss: %d", pss); + ALOGW("failed to get gl pss: %d", pss); return pss; } graphics_mem->gl = pss / 1024; pss = memtrack_proc_other_pss(p); if (pss < 0) { - ALOGE("failed to get other pss: %d", pss); + ALOGW("failed to get other pss: %d", pss); return pss; } graphics_mem->other = pss / 1024; @@ -199,7 +200,7 @@ static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_me struct memtrack_proc* p = memtrack_proc_new(); if (p == NULL) { - ALOGE("failed to create memtrack_proc"); + ALOGW("failed to create memtrack_proc"); return -1; } @@ -418,8 +419,6 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, stats[HEAP_GL].privateDirty = graphics_mem.gl; stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other; stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other; - } else { - ALOGE("Failed to read gpu memory"); } for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) { @@ -623,7 +622,7 @@ static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray o close(fd); if (len > 0) { buffer[len] = 0; - mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer); + mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024; } } @@ -633,7 +632,7 @@ static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray o } jlong* outArray = env->GetLongArrayElements(out, 0); if (outArray != NULL) { - for (int i=0; i<maxNum && tags[i]; i++) { + for (int i=0; i<maxNum; i++) { outArray[i] = mem[i]; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b41b5b569da9..4c73cd7df6f5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -773,6 +773,13 @@ android:description="@string/permdesc_bluetoothAdmin" android:label="@string/permlab_bluetoothAdmin" /> + <!-- Allows applications to pair bluetooth devices without user interaction --> + <permission android:name="android.permission.BLUETOOTH_PRIVILEGED" + android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK" + android:protectionLevel="system|signature" + android:description="@string/permdesc_bluetoothPriv" + android:label="@string/permlab_bluetoothPriv" /> + <!-- Allows bluetooth stack to access files @hide This should only be used by Bluetooth apk. --> diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png Binary files differindex f0d9b21443b2..a804a8a94564 100644 --- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png Binary files differindex 458137c57c22..778e4e67653d 100644 --- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png Binary files differindex f1209a29c504..77e69c72ab9a 100644 --- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png Binary files differindex 6c86258f063f..edecb6320de5 100644 --- a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png +++ b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6019e36fac5d..e39fd2a5be4f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -352,6 +352,10 @@ <!-- Wifi driver supports batched scan --> <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> + <!-- Default wifi country code. If the device is going to be sold in the US this + needs to be US. Uses ISO 3166 country code --> + <string translatable="false" name="config_wifi_default_country_code">US</string> + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d0c24e2529f9..6c334e241965 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1801,6 +1801,15 @@ <string name="permdesc_bluetoothAdmin" product="default">Allows the app to configure the local Bluetooth phone, and to discover and pair with remote devices.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_bluetoothPriv">allow Bluetooth pairing by Application</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bluetoothPriv" product="tablet">Allows the app to + pair with remote devices without user interaction.</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_bluetoothPriv" product="default">Allows the app to + pair with remote devices without user interaction.</string> + <string name="permlab_accessWimaxState">connect and disconnect from WiMAX</string> <string name="permdesc_accessWimaxState">Allows the app to determine whether WiMAX is enabled and information about any WiMAX networks that are diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f739bed85eed..c786888e8116 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -817,6 +817,7 @@ <java-symbol type="string" name="wifi_tether_configure_ssid_default" /> <java-symbol type="string" name="wifi_watchdog_network_disabled" /> <java-symbol type="string" name="wifi_watchdog_network_disabled_detailed" /> + <java-symbol type="string" name="config_wifi_default_country_code" /> <java-symbol type="string" name="imei" /> <java-symbol type="string" name="meid" /> <java-symbol type="string" name="granularity_label_character" /> diff --git a/docs/html/google/index.jd b/docs/html/google/index.jd index 4020cf451274..ce30bce8ba41 100644 --- a/docs/html/google/index.jd +++ b/docs/html/google/index.jd @@ -39,6 +39,7 @@ cloud messaging.</p> + <div style="margin-top:10px"> <div class="col-6 normal-links" style="margin-left:0"> @@ -48,7 +49,7 @@ cloud messaging.</p> </div> <h4><a href="{@docRoot}google/play-services/maps.html" >Google Maps</a></h4> - <p>The power of Google Maps is available to your app + <p>Include the power of Google Maps in your app with an embeddable map view. You can customize the map with markers and overlays, control the user's perspective, draw lines and shapes, and much more.</p> @@ -68,6 +69,17 @@ cloud messaging.</p> <div class="landing-cell"> <div class="cell-icon"> + <img src="{@docRoot}images/google/cloud-platform.png" width="40" > + </div> + <h4><a class="external-link" href="https://cloud.google.com/solutions/mobile" + >Google Cloud Platform</a></h4> + <p>Build and host the backend for your Android app at Google-scale. With an infrastructure + that is managed automatically, you can focus on your app. Then, scale to support + millions of users.</p> +</div> + +<div class="landing-cell"> + <div class="cell-icon"> <img src="{@docRoot}images/google/gcm-cloud.png" width="40" > </div> <h4><a href="{@docRoot}google/gcm/index.html" @@ -98,6 +110,18 @@ cloud messaging.</p> <div class="landing-cell"> <div class="cell-icon"> + <img src="{@docRoot}images/google/wallet.png" width="40" /> + </div> + <h4><a class="external-link" href="https://developers.google.com/commerce/wallet/instant-buy/" + >Google Wallet Instant Buy</a></h4> + <p>Provide fast and easy checkout in your app when selling physical goods and services. + Increase conversions by streamlining your purchase flow and reducing the amount of + information your customers need to enter. + </p> +</div> + +<div class="landing-cell"> + <div class="cell-icon"> <img src="{@docRoot}images/google/analytics.png" width="40" /> </div> <h4><a class="external-link" diff --git a/docs/html/guide/topics/ui/controls/checkbox.jd b/docs/html/guide/topics/ui/controls/checkbox.jd index 99140b55e84f..2a64e38d3bf4 100644 --- a/docs/html/guide/topics/ui/controls/checkbox.jd +++ b/docs/html/guide/topics/ui/controls/checkbox.jd @@ -94,7 +94,7 @@ must have a signature exactly as shown above. Specifically, the method must:</p> android.view.View} that was clicked)</li> </ul> -<p class="note"><strong>Tip:</strong> If you need to change the radio button state +<p class="note"><strong>Tip:</strong> If you need to change the checkbox state yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}), use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link android.widget.CompoundButton#toggle()} method.</p>
\ No newline at end of file diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd index 09767bff0754..d934c4b55e40 100644 --- a/docs/html/guide/topics/ui/dialogs.jd +++ b/docs/html/guide/topics/ui/dialogs.jd @@ -593,7 +593,7 @@ public class NoticeDialogFragment extends DialogFragment { .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // Send the negative button event back to the host activity - mListener.onDialogPositiveClick(NoticeDialogFragment.this); + mListener.onDialogNegativeClick(NoticeDialogFragment.this); } }); return builder.create(); diff --git a/docs/html/images/google/cloud-platform.png b/docs/html/images/google/cloud-platform.png Binary files differnew file mode 100644 index 000000000000..90df8edf1305 --- /dev/null +++ b/docs/html/images/google/cloud-platform.png diff --git a/docs/html/images/google/wallet.png b/docs/html/images/google/wallet.png Binary files differnew file mode 100644 index 000000000000..34cc11ebf5e4 --- /dev/null +++ b/docs/html/images/google/wallet.png diff --git a/docs/html/training/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd index d3f68e2713ca..f52865acff9e 100644 --- a/docs/html/training/articles/security-ssl.jd +++ b/docs/html/training/articles/security-ssl.jd @@ -250,7 +250,7 @@ due to a self-signed certificate, which means the server is behaving as its own This is similar to an unknown certificate authority, so you can use the same approach from the previous section.</p> -<p>You can create yout own {@link javax.net.ssl.TrustManager}, +<p>You can create your own {@link javax.net.ssl.TrustManager}, this time trusting the server certificate directly. This has all of the downsides discussed earlier of tying your app directly to a certificate, but can be done securely. However, you should be careful to make sure your self-signed certificate has a diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd index 0f667d7202ce..7240eecbe01e 100644 --- a/docs/html/training/articles/smp.jd +++ b/docs/html/training/articles/smp.jd @@ -1057,7 +1057,7 @@ an “impossible” state.</p> fix them. Before we do that, we need to discuss the use of a basic language feature.</p> -<h4 id="volatile">C/C+++ and "volatile"</h4> +<h4 id="volatile">C/C++ and "volatile"</h4> <p>When writing single-threaded code, declaring a variable “volatile” can be very useful. The compiler will not omit or reorder accesses to volatile diff --git a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd index 11a05e18f8c3..fb5096d3715f 100644 --- a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd +++ b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd @@ -49,7 +49,8 @@ to query the active network and determine if it has Internet connectivity.</p> (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); -boolean isConnected = activeNetwork.isConnectedOrConnecting();</pre> +boolean isConnected = activeNetwork != null && + activeNetwork.isConnectedOrConnecting();</pre> <h2 id="DetermineType">Determine the Type of your Internet Connection</h2> diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index a3c9dacea0f2..77ac235e8860 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -576,29 +576,6 @@ include the action bar on devices running Android 2.1 or higher." </li> </ul> </li> - <li class="nav-section"> - <div class="nav-section-header"> - <a href="<?cs var:toroot ?>training/id-auth/index.html" - description= - "How to remember the user by account, authenticate the user, acquire user permission - for the user's online data, and create custom accounts on the device." - >Remembering Users</a> - </div> - <ul> - <li><a href="<?cs var:toroot ?>training/id-auth/identify.html"> - Remembering Your User - </a> - </li> - <li><a href="<?cs var:toroot ?>training/id-auth/authenticate.html"> - Authenticating to OAuth2 Services - </a> - </li> - <li><a href="<?cs var:toroot ?>training/id-auth/custom_auth.html"> - Creating a Custom Account Type - </a> - </li> - </ul> - </li> <li class="nav-section"> <div class="nav-section-header"> diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index deba2ccff4cb..def9aa7f74e3 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1772,6 +1772,9 @@ public class MediaPlayer implements SubtitleController.Listener mSelectedSubtitleTrackIndex = -1; } setOnSubtitleDataListener(null); + if (track == null) { + return; + } for (int i = 0; i < mInbandSubtitleTracks.length; i++) { if (mInbandSubtitleTracks[i] == track) { Log.v(TAG, "Selecting subtitle track " + i); diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 512747913aa5..06af5de693b3 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -374,7 +374,7 @@ public class SoundPool { * Called when a sound has completed loading. * * @param soundPool SoundPool object from the load() method - * @param soundPool the sample ID of the sound loaded. + * @param sampleId the sample ID of the sound loaded. * @param status the status of the load operation (0 = success) */ public void onLoadComplete(SoundPool soundPool, int sampleId, int status); diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java index 80905615af21..13205bc26f54 100644 --- a/media/java/android/media/SubtitleController.java +++ b/media/java/android/media/SubtitleController.java @@ -21,6 +21,9 @@ import java.util.Vector; import android.content.Context; import android.media.SubtitleTrack.RenderingWidget; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.view.accessibility.CaptioningManager; /** @@ -37,6 +40,34 @@ public class SubtitleController { private SubtitleTrack mSelectedTrack; private boolean mShowing; private CaptioningManager mCaptioningManager; + private Handler mHandler; + + private static final int WHAT_SHOW = 1; + private static final int WHAT_HIDE = 2; + private static final int WHAT_SELECT_TRACK = 3; + private static final int WHAT_SELECT_DEFAULT_TRACK = 4; + + private final Handler.Callback mCallback = new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case WHAT_SHOW: + doShow(); + return true; + case WHAT_HIDE: + doHide(); + return true; + case WHAT_SELECT_TRACK: + doSelectTrack((SubtitleTrack)msg.obj); + return true; + case WHAT_SELECT_DEFAULT_TRACK: + doSelectDefaultTrack(); + return true; + default: + return false; + } + } + }; private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener = new CaptioningManager.CaptioningChangeListener() { @@ -112,7 +143,7 @@ public class SubtitleController { * in-band data from the {@link MediaPlayer}. However, this does * not change the subtitle visibility. * - * Must be called from the UI thread. + * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} * * @param track The subtitle track to select. This must be one of the * tracks in {@link #getTracks}. @@ -122,9 +153,15 @@ public class SubtitleController { if (track != null && !mTracks.contains(track)) { return false; } + + processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track)); + return true; + } + + private void doSelectTrack(SubtitleTrack track) { mTrackIsExplicit = true; if (mSelectedTrack == track) { - return true; + return; } if (mSelectedTrack != null) { @@ -145,7 +182,6 @@ public class SubtitleController { if (mListener != null) { mListener.onSubtitleTrackSelected(track); } - return true; } /** @@ -170,8 +206,6 @@ public class SubtitleController { * * The default values for these flags are DEFAULT=no, AUTOSELECT=yes * and FORCED=no. - * - * Must be called from the UI thread. */ public SubtitleTrack getDefaultTrack() { SubtitleTrack bestTrack = null; @@ -226,8 +260,12 @@ public class SubtitleController { private boolean mTrackIsExplicit = false; private boolean mVisibilityIsExplicit = false; - /** @hide - called from UI thread */ + /** @hide - should be called from anchor thread */ public void selectDefaultTrack() { + processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK)); + } + + private void doSelectDefaultTrack() { if (mTrackIsExplicit) { // If track selection is explicit, but visibility // is not, it falls back to the captioning setting @@ -259,8 +297,9 @@ public class SubtitleController { } } - /** @hide - called from UI thread */ + /** @hide - must be called from anchor thread */ public void reset() { + checkAnchorLooper(); hide(); selectTrack(null); mTracks.clear(); @@ -301,9 +340,13 @@ public class SubtitleController { /** * Show the selected (or default) subtitle track. * - * Must be called from the UI thread. + * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} */ public void show() { + processOnAnchor(mHandler.obtainMessage(WHAT_SHOW)); + } + + private void doShow() { mShowing = true; mVisibilityIsExplicit = true; if (mSelectedTrack != null) { @@ -314,9 +357,13 @@ public class SubtitleController { /** * Hide the selected (or default) subtitle track. * - * Must be called from the UI thread. + * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper} */ public void hide() { + processOnAnchor(mHandler.obtainMessage(WHAT_HIDE)); + } + + private void doHide() { mVisibilityIsExplicit = true; if (mSelectedTrack != null) { mSelectedTrack.hide(); @@ -384,25 +431,53 @@ public class SubtitleController { * @hide */ public void setSubtitleWidget(RenderingWidget subtitleWidget); + + /** + * Anchors provide the looper on which all track visibility changes + * (track.show/hide, setSubtitleWidget) will take place. + * @hide + */ + public Looper getSubtitleLooper(); } private Anchor mAnchor; - /** @hide - called from UI thread */ + /** + * @hide - called from anchor's looper (if any, both when unsetting and + * setting) + */ public void setAnchor(Anchor anchor) { if (mAnchor == anchor) { return; } if (mAnchor != null) { + checkAnchorLooper(); mAnchor.setSubtitleWidget(null); } mAnchor = anchor; + mHandler = null; if (mAnchor != null) { + mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback); + checkAnchorLooper(); mAnchor.setSubtitleWidget(getRenderingWidget()); } } + private void checkAnchorLooper() { + assert mHandler != null : "Should have a looper already"; + assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper"; + } + + private void processOnAnchor(Message m) { + assert mHandler != null : "Should have a looper already"; + if (Looper.myLooper() == mHandler.getLooper()) { + mHandler.dispatchMessage(m); + } else { + mHandler.sendMessage(m); + } + } + public interface Listener { /** * Called when a subtitle track has been selected. diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png Binary files differindex 02408741c9cf..904d5253672c 100644 --- a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png Binary files differnew file mode 100644 index 000000000000..0d75172a1eb1 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png Binary files differnew file mode 100644 index 000000000000..403eddb4f90a --- /dev/null +++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png Binary files differindex 02408741c9cf..068619be0b43 100644 --- a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png +++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png Binary files differnew file mode 100644 index 000000000000..0d75172a1eb1 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png Binary files differnew file mode 100644 index 000000000000..9a9cf5ede773 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png Binary files differindex 02408741c9cf..e38a8685dcea 100644 --- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png +++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png Binary files differnew file mode 100644 index 000000000000..0d75172a1eb1 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png Binary files differnew file mode 100644 index 000000000000..205c34bcbe60 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png Binary files differindex 02408741c9cf..0b332e410a77 100644 --- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png +++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png Binary files differnew file mode 100644 index 000000000000..32b5f9880a89 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png Binary files differnew file mode 100644 index 000000000000..f47d50a834b8 --- /dev/null +++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml index 3bea1663c1f4..851061fe9b51 100644 --- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml +++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml @@ -21,17 +21,18 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dip" - android:paddingBottom="8dip" - android:orientation="horizontal"> + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:gravity="center_vertical" + android:orientation="horizontal" + android:baselineAligned="false"> <FrameLayout android:id="@android:id/icon" android:layout_width="@dimen/icon_size" android:layout_height="@dimen/icon_size" android:layout_marginStart="12dp" - android:layout_marginEnd="20dp" - android:layout_gravity="center_vertical"> + android:layout_marginEnd="20dp"> <ImageView android:id="@+id/icon_mime" @@ -49,11 +50,11 @@ </FrameLayout> + <!-- This is the one special case where we want baseline alignment! --> <LinearLayout - android:layout_width="0dip" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_gravity="center_vertical" android:orientation="horizontal"> <TextView diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml index 78735fdc39d3..9286277958b1 100644 --- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml +++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml @@ -17,7 +17,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="horizontal"> + android:orientation="horizontal" + android:baselineAligned="false"> <FrameLayout android:layout_width="wrap_content" @@ -47,7 +48,7 @@ <com.android.documentsui.DirectoryContainerView android:id="@+id/container_directory" android:layout_width="match_parent" - android:layout_height="0dip" + android:layout_height="0dp" android:layout_weight="1" /> <FrameLayout diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml index 9937c390780b..2ef7e9c1f636 100644 --- a/packages/DocumentsUI/res/layout/activity.xml +++ b/packages/DocumentsUI/res/layout/activity.xml @@ -27,7 +27,7 @@ <com.android.documentsui.DirectoryContainerView android:id="@+id/container_directory" android:layout_width="match_parent" - android:layout_height="0dip" + android:layout_height="0dp" android:layout_weight="1" /> <FrameLayout diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml index 09782d9349b7..c3a3da07e933 100644 --- a/packages/DocumentsUI/res/layout/fragment_roots.xml +++ b/packages/DocumentsUI/res/layout/fragment_roots.xml @@ -18,4 +18,4 @@ android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" - android:divider="@null" /> + android:divider="@drawable/ic_drawer_hairline_divider" /> diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml index 570b517f30fd..891f0a07129f 100644 --- a/packages/DocumentsUI/res/layout/fragment_save.xml +++ b/packages/DocumentsUI/res/layout/fragment_save.xml @@ -29,6 +29,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" + android:baselineAligned="false" android:gravity="center_vertical" android:background="#ddd" android:minHeight="?android:attr/listPreferredItemHeightSmall"> @@ -44,7 +45,7 @@ <EditText android:id="@android:id/title" - android:layout_width="0dip" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml index b745bb94316d..bb5dce1306d7 100644 --- a/packages/DocumentsUI/res/layout/item_doc_grid.xml +++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml @@ -28,37 +28,25 @@ <FrameLayout android:layout_width="match_parent" - android:layout_height="0dip" + android:layout_height="0dp" android:layout_weight="1" android:layout_marginBottom="6dp" - android:background="#fff"> + android:background="#fff" + android:foreground="@drawable/ic_grid_gradient_bg" + android:foregroundGravity="fill"> - <FrameLayout - android:id="@android:id/icon" + <ImageView + android:id="@+id/icon_mime" android:layout_width="match_parent" - android:layout_height="match_parent"> - - <ImageView - android:id="@+id/icon_mime" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="centerInside" - android:contentDescription="@null" /> - - <ImageView - android:id="@+id/icon_thumb" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="centerCrop" - android:contentDescription="@null" /> - - </FrameLayout> + android:layout_height="match_parent" + android:scaleType="centerInside" + android:contentDescription="@null" /> <ImageView + android:id="@+id/icon_thumb" android:layout_width="match_parent" android:layout_height="match_parent" - android:scaleType="fitXY" - android:src="@drawable/ic_grid_gradient_bg" + android:scaleType="centerCrop" android:contentDescription="@null" /> </FrameLayout> @@ -67,7 +55,9 @@ android:id="@+id/line1" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center_vertical" android:orientation="horizontal" + android:baselineAligned="false" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> @@ -85,7 +75,7 @@ android:id="@android:id/icon1" android:layout_width="@dimen/root_icon_size" android:layout_height="@dimen/root_icon_size" - android:layout_marginStart="8dip" + android:layout_marginStart="8dp" android:scaleType="centerInside" android:contentDescription="@null" /> @@ -95,16 +85,17 @@ android:id="@+id/line2" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center_vertical" android:orientation="horizontal" + android:baselineAligned="false" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:minWidth="80dp" + android:layout_weight="0.5" android:singleLine="true" android:ellipsize="marquee" android:textAlignment="viewStart" @@ -112,26 +103,20 @@ <TextView android:id="@+id/size" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" + android:layout_weight="0.5" android:layout_marginStart="8dp" - android:minWidth="80dp" android:singleLine="true" android:ellipsize="marquee" android:textAlignment="viewStart" style="@style/TextAppearance.Small" /> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" /> - <ImageView android:id="@android:id/icon2" android:layout_width="@dimen/root_icon_size" android:layout_height="@dimen/root_icon_size" - android:layout_marginStart="8dip" + android:layout_marginStart="8dp" android:scaleType="centerInside" android:contentDescription="@null" android:visibility="gone" /> diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml index 84fda9d36ad3..4c5b2e304f6e 100644 --- a/packages/DocumentsUI/res/layout/item_doc_list.xml +++ b/packages/DocumentsUI/res/layout/item_doc_list.xml @@ -21,17 +21,18 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dip" - android:paddingBottom="8dip" - android:orientation="horizontal"> + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:gravity="center_vertical" + android:orientation="horizontal" + android:baselineAligned="false"> <FrameLayout android:id="@android:id/icon" android:layout_width="@dimen/icon_size" android:layout_height="@dimen/icon_size" android:layout_marginStart="12dp" - android:layout_marginEnd="20dp" - android:layout_gravity="center_vertical"> + android:layout_marginEnd="20dp"> <ImageView android:id="@+id/icon_mime" @@ -50,20 +51,20 @@ </FrameLayout> <LinearLayout - android:layout_width="0dip" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_gravity="center_vertical" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="horizontal" + android:baselineAligned="false"> <TextView android:id="@android:id/title" - android:layout_width="0dip" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" @@ -75,7 +76,7 @@ android:id="@android:id/icon1" android:layout_width="@dimen/root_icon_size" android:layout_height="@dimen/root_icon_size" - android:layout_marginStart="8dip" + android:layout_marginStart="8dp" android:scaleType="centerInside" android:contentDescription="@null" /> @@ -85,13 +86,15 @@ android:id="@+id/line2" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:gravity="center_vertical" + android:orientation="horizontal" + android:baselineAligned="false"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" + android:layout_weight="0.25" android:minWidth="70dp" android:singleLine="true" android:ellipsize="marquee" @@ -100,11 +103,11 @@ <TextView android:id="@+id/size" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:minWidth="70dp" + android:layout_weight="0.25" android:layout_marginStart="8dp" + android:minWidth="70dp" android:singleLine="true" android:ellipsize="marquee" android:textAlignment="viewStart" @@ -114,8 +117,7 @@ android:id="@android:id/summary" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="center_vertical" + android:layout_weight="0.5" android:layout_marginStart="8dp" android:singleLine="true" android:ellipsize="marquee" diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml index 21be13744753..0bf6137b7464 100644 --- a/packages/DocumentsUI/res/layout/item_loading_grid.xml +++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml @@ -20,8 +20,8 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dip" - android:paddingBottom="8dip" + android:paddingTop="8dp" + android:paddingBottom="8dp" android:orientation="horizontal"> <ProgressBar diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/DocumentsUI/res/layout/item_loading_list.xml index 7da71e3cb4f6..cdcd01d2ad94 100644 --- a/packages/DocumentsUI/res/layout/item_loading_list.xml +++ b/packages/DocumentsUI/res/layout/item_loading_list.xml @@ -20,9 +20,8 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dip" - android:paddingBottom="8dip" - android:orientation="horizontal"> + android:paddingTop="8dp" + android:paddingBottom="8dp"> <ProgressBar android:layout_width="wrap_content" diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml index ffda98c1205f..2bcbc2d30c2f 100644 --- a/packages/DocumentsUI/res/layout/item_message_list.xml +++ b/packages/DocumentsUI/res/layout/item_message_list.xml @@ -21,15 +21,16 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dip" - android:paddingBottom="8dip" - android:orientation="horizontal"> + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:orientation="horizontal" + android:baselineAligned="false"> <ImageView android:id="@android:id/icon" android:layout_width="@android:dimen/app_icon_size" android:layout_height="@android:dimen/app_icon_size" - android:layout_marginEnd="8dip" + android:layout_marginEnd="8dp" android:layout_gravity="center_vertical" android:scaleType="centerInside" android:contentDescription="@null" /> diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml index 98d78dac545f..9b52d85ca45e 100644 --- a/packages/DocumentsUI/res/layout/item_root.xml +++ b/packages/DocumentsUI/res/layout/item_root.xml @@ -22,13 +22,14 @@ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:gravity="center_vertical" android:orientation="horizontal" + android:baselineAligned="false" android:background="@drawable/item_root"> <ImageView android:id="@android:id/icon" android:layout_width="@dimen/icon_size" android:layout_height="@dimen/icon_size" - android:layout_marginEnd="8dip" + android:layout_marginEnd="8dp" android:scaleType="centerInside" android:contentDescription="@null" /> diff --git a/packages/DocumentsUI/res/layout/item_root_spacer.xml b/packages/DocumentsUI/res/layout/item_root_spacer.xml new file mode 100644 index 000000000000..7d96ac8a5c94 --- /dev/null +++ b/packages/DocumentsUI/res/layout/item_root_spacer.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/ic_drawer_tall_divider" /> diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml index 7eb100a245b0..58016f1fe962 100644 --- a/packages/DocumentsUI/res/layout/item_title.xml +++ b/packages/DocumentsUI/res/layout/item_title.xml @@ -21,7 +21,8 @@ android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:gravity="center_vertical" - android:orientation="horizontal"> + android:orientation="horizontal" + android:baselineAligned="false"> <ImageView android:id="@+id/subdir" diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index 9d92cd89596f..48bfaf0d7c34 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; @@ -35,6 +36,8 @@ import android.widget.Toast; import com.android.documentsui.model.DocumentInfo; +import java.io.FileNotFoundException; + /** * Dialog to create a new directory. */ @@ -64,24 +67,45 @@ public class CreateDirectoryFragment extends DialogFragment { @Override public void onClick(DialogInterface dialog, int which) { final String displayName = text1.getText().toString(); - - final DocumentsActivity activity = (DocumentsActivity) getActivity(); - final DocumentInfo cwd = activity.getCurrentDirectory(); - - try { - final Uri childUri = DocumentsContract.createDocument( - resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName); - - // Navigate into newly created child - final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri); - activity.onDocumentPicked(childDoc); - } catch (Exception e) { - Toast.makeText(context, R.string.create_error, Toast.LENGTH_SHORT).show(); - } + new CreateDirectoryTask(displayName).execute(); } }); builder.setNegativeButton(android.R.string.cancel, null); return builder.create(); } + + private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> { + private final String mDisplayName; + + public CreateDirectoryTask(String displayName) { + mDisplayName = displayName; + } + + @Override + protected DocumentInfo doInBackground(Void... params) { + final DocumentsActivity activity = (DocumentsActivity) getActivity(); + final ContentResolver resolver = activity.getContentResolver(); + + final DocumentInfo cwd = activity.getCurrentDirectory(); + final Uri childUri = DocumentsContract.createDocument( + resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName); + try { + return DocumentInfo.fromUri(resolver, childUri); + } catch (FileNotFoundException e) { + return null; + } + } + + @Override + protected void onPostExecute(DocumentInfo result) { + final DocumentsActivity activity = (DocumentsActivity) getActivity(); + if (result != null) { + // Navigate into newly created child + activity.onDocumentPicked(result); + } else { + Toast.makeText(activity, R.string.create_error, Toast.LENGTH_SHORT).show(); + } + } + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 138f523ce56d..1f11aed91358 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -742,7 +742,6 @@ public class DirectoryFragment extends Fragment { final View line1 = convertView.findViewById(R.id.line1); final View line2 = convertView.findViewById(R.id.line2); - final View icon = convertView.findViewById(android.R.id.icon); final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime); final ImageView iconThumb = (ImageView) convertView.findViewById(R.id.icon_thumb); final TextView title = (TextView) convertView.findViewById(android.R.id.title); @@ -786,10 +785,12 @@ public class DirectoryFragment extends Fragment { // loaded in background. if (cacheHit) { iconMime.setAlpha(0f); + iconMime.setImageDrawable(null); iconThumb.setAlpha(1f); } else { iconMime.setAlpha(1f); iconThumb.setAlpha(0f); + iconThumb.setImageDrawable(null); if (docIcon != 0) { iconMime.setImageDrawable( IconUtils.loadPackageIcon(context, docAuthority, docIcon)); @@ -895,12 +896,14 @@ public class DirectoryFragment extends Fragment { final boolean enabled = isDocumentEnabled(docMimeType, docFlags); if (enabled) { setEnabledRecursive(convertView, true); - icon.setAlpha(1f); + iconMime.setAlpha(1f); + iconThumb.setAlpha(1f); if (icon1 != null) icon1.setAlpha(1f); if (icon2 != null) icon2.setAlpha(1f); } else { setEnabledRecursive(convertView, false); - icon.setAlpha(0.5f); + iconMime.setAlpha(0.5f); + iconThumb.setAlpha(0.5f); if (icon1 != null) icon1.setAlpha(0.5f); if (icon2 != null) icon2.setAlpha(0.5f); } @@ -991,10 +994,11 @@ public class DirectoryFragment extends Fragment { mIconThumb.setTag(null); mIconThumb.setImageBitmap(result); - mIconMime.setAlpha(1f); + final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f; + mIconMime.setAlpha(targetAlpha); mIconMime.animate().alpha(0f).start(); mIconThumb.setAlpha(0f); - mIconThumb.animate().alpha(1f).start(); + mIconThumb.animate().alpha(targetAlpha).start(); } } } @@ -1060,16 +1064,16 @@ public class DirectoryFragment extends Fragment { private boolean isDocumentEnabled(String docMimeType, int docFlags) { final State state = getDisplayState(DirectoryFragment.this); - // Read-only files are disabled when creating - if (state.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) { - return false; - } - // Directories are always enabled if (Document.MIME_TYPE_DIR.equals(docMimeType)) { return true; } + // Read-only files are disabled when creating + if (state.action == ACTION_CREATE && (docFlags & Document.FLAG_SUPPORTS_WRITE) == 0) { + return false; + } + return MimePredicate.mimeMatches(state.acceptMimes, docMimeType); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index 8627ecfaa2f7..0b3ecf84900e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -63,6 +63,9 @@ class DirectoryResult implements AutoCloseable { } public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { + + private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; + private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); private final int mType; @@ -164,8 +167,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { if (mType == DirectoryFragment.TYPE_SEARCH) { // Filter directories out of search results, for now - cursor = new FilteringCursorWrapper(cursor, null, new String[] { - Document.MIME_TYPE_DIR }); + cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); } else { // Normal directories should have sorting applied cursor = new SortingCursorWrapper(cursor, result.sortOrder); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 72fdc57f9cab..4caec8fb0ad2 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -854,14 +854,7 @@ public class DocumentsActivity extends Activity { mState.stackTouched = true; if (!mRoots.isRecentsRoot(root)) { - try { - final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId); - final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri); - mState.stack.push(doc); - mState.stackTouched = true; - onCurrentDirectoryChanged(ANIM_SIDE); - } catch (FileNotFoundException e) { - } + new PickRootTask(root).execute(); } else { onCurrentDirectoryChanged(ANIM_SIDE); } @@ -871,6 +864,34 @@ public class DocumentsActivity extends Activity { } } + private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> { + private RootInfo mRoot; + + public PickRootTask(RootInfo root) { + mRoot = root; + } + + @Override + protected DocumentInfo doInBackground(Void... params) { + try { + final Uri uri = DocumentsContract.buildDocumentUri( + mRoot.authority, mRoot.documentId); + return DocumentInfo.fromUri(getContentResolver(), uri); + } catch (FileNotFoundException e) { + return null; + } + } + + @Override + protected void onPostExecute(DocumentInfo result) { + if (result != null) { + mState.stack.push(result); + mState.stackTouched = true; + onCurrentDirectoryChanged(ANIM_SIDE); + } + } + } + public void onAppPicked(ResolveInfo info) { final Intent intent = new Intent(getIntent()); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT); @@ -909,7 +930,7 @@ public class DocumentsActivity extends Activity { onCurrentDirectoryChanged(ANIM_DOWN); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) { // Explicit file picked, return - onFinished(doc.derivedUri); + new ExistingFinishTask(doc.derivedUri).execute(); } else if (mState.action == ACTION_CREATE) { // Replace selected file SaveFragment.get(fm).setReplaceTarget(doc); @@ -943,29 +964,19 @@ public class DocumentsActivity extends Activity { for (int i = 0; i < size; i++) { uris[i] = docs.get(i).derivedUri; } - onFinished(uris); + new ExistingFinishTask(uris).execute(); } } public void onSaveRequested(DocumentInfo replaceTarget) { - onFinished(replaceTarget.derivedUri); + new ExistingFinishTask(replaceTarget.derivedUri).execute(); } public void onSaveRequested(String mimeType, String displayName) { - final DocumentInfo cwd = getCurrentDirectory(); - - final Uri childUri = DocumentsContract.createDocument( - getContentResolver(), cwd.derivedUri, mimeType, displayName); - if (childUri != null) { - onFinished(childUri); - } else { - Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show(); - } + new CreateFinishTask(mimeType, displayName).execute(); } - private void onFinished(Uri... uris) { - Log.d(TAG, "onFinished() " + Arrays.toString(uris)); - + private void saveStackBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); @@ -973,6 +984,7 @@ public class DocumentsActivity extends Activity { if (mState.action == ACTION_CREATE) { // Remember stack for last create values.clear(); + values.put(RecentColumns.KEY, mState.stack.buildKey()); values.put(RecentColumns.STACK, rawStack); resolver.insert(RecentsProvider.buildRecent(), values); } @@ -983,6 +995,10 @@ public class DocumentsActivity extends Activity { values.put(ResumeColumns.STACK, rawStack); values.put(ResumeColumns.EXTERNAL, 0); resolver.insert(RecentsProvider.buildResume(packageName), values); + } + + private void onFinished(Uri... uris) { + Log.d(TAG, "onFinished() " + Arrays.toString(uris)); final Intent intent = new Intent(); if (uris.length == 1) { @@ -1008,6 +1024,56 @@ public class DocumentsActivity extends Activity { finish(); } + private class CreateFinishTask extends AsyncTask<Void, Void, Uri> { + private final String mMimeType; + private final String mDisplayName; + + public CreateFinishTask(String mimeType, String displayName) { + mMimeType = mimeType; + mDisplayName = displayName; + } + + @Override + protected Uri doInBackground(Void... params) { + final DocumentInfo cwd = getCurrentDirectory(); + final Uri childUri = DocumentsContract.createDocument( + getContentResolver(), cwd.derivedUri, mMimeType, mDisplayName); + if (childUri != null) { + saveStackBlocking(); + } + return childUri; + } + + @Override + protected void onPostExecute(Uri result) { + if (result != null) { + onFinished(result); + } else { + Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT) + .show(); + } + } + } + + private class ExistingFinishTask extends AsyncTask<Void, Void, Void> { + private final Uri[] mUris; + + public ExistingFinishTask(Uri... uris) { + mUris = uris; + } + + @Override + protected Void doInBackground(Void... params) { + saveStackBlocking(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + onFinished(mUris); + } + } + public static class State implements android.os.Parcelable { public int action; public String[] acceptMimes; diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java index 5f56963ae463..52d816f1f968 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java @@ -34,10 +34,15 @@ public class FilteringCursorWrapper extends AbstractCursor { private int mCount; public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) { - this(cursor, acceptMimes, null); + this(cursor, acceptMimes, null, Long.MIN_VALUE); } public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) { + this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE); + } + + public FilteringCursorWrapper( + Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) { mCursor = cursor; final int count = cursor.getCount(); @@ -47,9 +52,14 @@ public class FilteringCursorWrapper extends AbstractCursor { while (cursor.moveToNext()) { final String mimeType = cursor.getString( cursor.getColumnIndex(Document.COLUMN_MIME_TYPE)); + final long lastModified = cursor.getLong( + cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED)); if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) { continue; } + if (lastModified < rejectBefore) { + continue; + } if (MimePredicate.mimeMatches(acceptMimes, mimeType)) { mPosition[mCount++] = cursor.getPosition(); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index e39045677322..9a4fb7da725f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -26,9 +26,11 @@ import android.content.Context; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; +import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; +import android.text.format.DateUtils; import android.util.Log; import com.android.documentsui.DocumentsActivity.State; @@ -54,17 +56,23 @@ import java.util.concurrent.TimeUnit; public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { private static final boolean LOGD = true; - public static final int MAX_OUTSTANDING_RECENTS = 2; + // TODO: adjust for svelte devices + // TODO: add support for oneway queries to avoid wedging loader + private static final int MAX_OUTSTANDING_RECENTS = 2; /** * Time to wait for first pass to complete before returning partial results. */ - public static final int MAX_FIRST_PASS_WAIT_MILLIS = 500; + private static final int MAX_FIRST_PASS_WAIT_MILLIS = 500; - /** - * Maximum documents from a single root. - */ - public static final int MAX_DOCS_FROM_ROOT = 64; + /** Maximum documents from a single root. */ + private static final int MAX_DOCS_FROM_ROOT = 64; + + /** Ignore documents older than this age. */ + private static final long REJECT_OLDER_THAN = 45 * DateUtils.DAY_IN_MILLIS; + + /** MIME types that should always be excluded from recents. */ + private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; private static final ExecutorService sExecutor = buildExecutor(); @@ -173,6 +181,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { } } + final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN; + // Collect all finished tasks List<Cursor> cursors = Lists.newArrayList(); for (RecentTask task : mTasks.values()) { @@ -180,7 +190,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { try { final Cursor cursor = task.get(); final FilteringCursorWrapper filtered = new FilteringCursorWrapper( - cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) { + cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) { @Override public void close() { // Ignored, since we manage cursor lifecycle internally @@ -203,11 +213,22 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { final DirectoryResult result = new DirectoryResult(); result.sortOrder = SORT_ORDER_LAST_MODIFIED; - if (cursors.size() > 0) { - final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()])); - final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder); - result.cursor = sorted; + // Hint to UI if we're still loading + final Bundle extras = new Bundle(); + if (cursors.size() != mTasks.size()) { + extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); } + + final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()])); + final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) { + @Override + public Bundle getExtras() { + return extras; + } + }; + + result.cursor = sorted; + return result; } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java index c975382b8bae..39541731eda5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java @@ -66,6 +66,7 @@ import java.util.List; */ public class RecentsCreateFragment extends Fragment { + private View mEmptyView; private ListView mListView; private DocumentStackAdapter mAdapter; @@ -87,6 +88,8 @@ public class RecentsCreateFragment extends Fragment { final View view = inflater.inflate(R.layout.fragment_directory, container, false); + mEmptyView = view.findViewById(android.R.id.empty); + mListView = (ListView) view.findViewById(R.id.list); mListView.setOnItemClickListener(mItemListener); @@ -189,6 +192,13 @@ public class RecentsCreateFragment extends Fragment { public void swapStacks(List<DocumentStack> stacks) { mStacks = stacks; + + if (isEmpty()) { + mEmptyView.setVisibility(View.VISIBLE); + } else { + mEmptyView.setVisibility(View.GONE); + } + notifyDataSetChanged(); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java index 7386cae9b84e..4313fa71c490 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java @@ -33,7 +33,7 @@ import android.util.Log; public class RecentsProvider extends ContentProvider { private static final String TAG = "RecentsProvider"; - public static final long MAX_HISTORY_IN_MILLIS = DateUtils.DAY_IN_MILLIS * 45; + public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS; private static final String AUTHORITY = "com.android.documentsui.recents"; @@ -56,6 +56,7 @@ public class RecentsProvider extends ContentProvider { public static final String TABLE_RESUME = "resume"; public static class RecentColumns { + public static final String KEY = "key"; public static final String STACK = "stack"; public static final String TIMESTAMP = "timestamp"; } @@ -99,16 +100,18 @@ public class RecentsProvider extends ContentProvider { private static final int VERSION_INIT = 1; private static final int VERSION_AS_BLOB = 3; private static final int VERSION_ADD_EXTERNAL = 4; + private static final int VERSION_ADD_RECENT_KEY = 5; public DatabaseHelper(Context context) { - super(context, DB_NAME, null, VERSION_ADD_EXTERNAL); + super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" + - RecentColumns.STACK + " BLOB PRIMARY KEY ON CONFLICT REPLACE," + + RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," + + RecentColumns.STACK + " BLOB DEFAULT NULL," + RecentColumns.TIMESTAMP + " INTEGER" + ")"); diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index 15af8aab45a7..e3908e9ad515 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -99,7 +99,8 @@ public class RootsCache { */ public void updateAsync() { // Special root for recents - mRecentsRoot.rootType = Root.ROOT_TYPE_SHORTCUT; + mRecentsRoot.authority = null; + mRecentsRoot.rootId = null; mRecentsRoot.icon = R.drawable.ic_root_recent; mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE; mRecentsRoot.title = mContext.getString(R.string.root_recent); diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index d6026226e499..2fb12bb29233 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -26,7 +26,6 @@ import android.content.Loader; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; -import android.provider.DocumentsContract.Root; import android.text.TextUtils; import android.text.format.Formatter; import android.view.LayoutInflater; @@ -37,16 +36,16 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; -import android.widget.Space; import android.widget.TextView; import com.android.documentsui.DocumentsActivity.State; -import com.android.documentsui.SectionedListAdapter.SectionAdapter; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Objects; +import com.google.common.collect.Lists; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -56,7 +55,7 @@ import java.util.List; public class RootsFragment extends Fragment { private ListView mList; - private SectionedRootsAdapter mAdapter; + private RootsAdapter mAdapter; private LoaderCallbacks<Collection<RootInfo>> mCallbacks; @@ -112,7 +111,7 @@ public class RootsFragment extends Fragment { final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS); - mAdapter = new SectionedRootsAdapter(context, result, includeApps); + mAdapter = new RootsAdapter(context, result, includeApps); mList.setAdapter(mAdapter); onCurrentRootChanged(); @@ -154,136 +153,148 @@ public class RootsFragment extends Fragment { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this); - final Object item = mAdapter.getItem(position); - if (item instanceof RootInfo) { - activity.onRootPicked((RootInfo) item, true); - } else if (item instanceof ResolveInfo) { - activity.onAppPicked((ResolveInfo) item); + final Item item = mAdapter.getItem(position); + if (item instanceof RootItem) { + activity.onRootPicked(((RootItem) item).root, true); + } else if (item instanceof AppItem) { + activity.onAppPicked(((AppItem) item).info); } else { throw new IllegalStateException("Unknown root: " + item); } } }; - private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter { - public RootsAdapter(Context context) { - super(context, 0); + private static abstract class Item { + private final int mLayoutId; + + public Item(int layoutId) { + mLayoutId = layoutId; } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final Context context = parent.getContext(); + public View getView(View convertView, ViewGroup parent) { if (convertView == null) { - convertView = LayoutInflater.from(context) - .inflate(R.layout.item_root, parent, false); + convertView = LayoutInflater.from(parent.getContext()) + .inflate(mLayoutId, parent, false); } + bindView(convertView); + return convertView; + } + + public abstract void bindView(View convertView); + } + private static class RootItem extends Item { + public final RootInfo root; + + public RootItem(RootInfo root) { + super(R.layout.item_root); + this.root = root; + } + + @Override + public void bindView(View convertView) { final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); final TextView title = (TextView) convertView.findViewById(android.R.id.title); final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); - final RootInfo root = getItem(position); + final Context context = convertView.getContext(); icon.setImageDrawable(root.loadIcon(context)); title.setText(root.title); - // Device summary is always available space - final String summaryText; - if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) { + // Show available space if no summary + String summaryText = root.summary; + if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) { summaryText = context.getString(R.string.root_available_bytes, Formatter.formatFileSize(context, root.availableBytes)); - } else { - summaryText = root.summary; } summary.setText(summaryText); summary.setVisibility(TextUtils.isEmpty(summaryText) ? View.GONE : View.VISIBLE); + } + } - return convertView; + private static class SpacerItem extends Item { + public SpacerItem() { + super(R.layout.item_root_spacer); } @Override - public View getHeaderView(View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = new Space(parent.getContext()); - } - return convertView; + public void bindView(View convertView) { + // Nothing to bind } } - private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter { - public AppsAdapter(Context context) { - super(context, 0); + private static class AppItem extends Item { + public final ResolveInfo info; + + public AppItem(ResolveInfo info) { + super(R.layout.item_root); + this.info = info; } @Override - public View getView(int position, View convertView, ViewGroup parent) { - final Context context = parent.getContext(); - final PackageManager pm = context.getPackageManager(); - if (convertView == null) { - convertView = LayoutInflater.from(context) - .inflate(R.layout.item_root, parent, false); - } - + public void bindView(View convertView) { final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); final TextView title = (TextView) convertView.findViewById(android.R.id.title); final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); - final ResolveInfo info = getItem(position); + final PackageManager pm = convertView.getContext().getPackageManager(); icon.setImageDrawable(info.loadIcon(pm)); title.setText(info.loadLabel(pm)); // TODO: match existing summary behavior from disambig dialog summary.setVisibility(View.GONE); - - return convertView; } + } - @Override - public View getHeaderView(View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_root_header, parent, false); - } + private static class RootsAdapter extends ArrayAdapter<Item> { + public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) { + super(context, 0); - final TextView title = (TextView) convertView.findViewById(android.R.id.title); - title.setText(R.string.root_type_apps); + RootItem recents = null; + RootItem images = null; + RootItem videos = null; + RootItem audio = null; + RootItem downloads = null; - return convertView; - } - } - - private static class SectionedRootsAdapter extends SectionedListAdapter { - private final RootsAdapter mRecent; - private final RootsAdapter mServices; - private final RootsAdapter mShortcuts; - private final RootsAdapter mDevices; - private final AppsAdapter mApps; - - public SectionedRootsAdapter( - Context context, Collection<RootInfo> roots, Intent includeApps) { - mRecent = new RootsAdapter(context); - mServices = new RootsAdapter(context); - mShortcuts = new RootsAdapter(context); - mDevices = new RootsAdapter(context); - mApps = new AppsAdapter(context); + final List<RootInfo> clouds = Lists.newArrayList(); + final List<RootInfo> locals = Lists.newArrayList(); for (RootInfo root : roots) { - if (root.authority == null) { - mRecent.add(root); - continue; + if (root.isRecents()) { + recents = new RootItem(root); + } else if (root.isExternalStorage()) { + locals.add(root); + } else if (root.isDownloads()) { + downloads = new RootItem(root); + } else if (root.isImages()) { + images = new RootItem(root); + } else if (root.isVideos()) { + videos = new RootItem(root); + } else if (root.isAudio()) { + audio = new RootItem(root); + } else { + clouds.add(root); } + } - switch (root.rootType) { - case Root.ROOT_TYPE_SERVICE: - mServices.add(root); - break; - case Root.ROOT_TYPE_SHORTCUT: - mShortcuts.add(root); - break; - case Root.ROOT_TYPE_DEVICE: - mDevices.add(root); - break; - } + final RootComparator comp = new RootComparator(); + Collections.sort(clouds, comp); + Collections.sort(locals, comp); + + if (recents != null) add(recents); + + for (RootInfo cloud : clouds) { + add(new RootItem(cloud)); + } + + if (images != null) add(images); + if (videos != null) add(videos); + if (audio != null) add(audio); + if (downloads != null) add(downloads); + + for (RootInfo local : locals) { + add(new RootItem(local)); } if (includeApps != null) { @@ -291,35 +302,54 @@ public class RootsFragment extends Fragment { final List<ResolveInfo> infos = pm.queryIntentActivities( includeApps, PackageManager.MATCH_DEFAULT_ONLY); + final List<AppItem> apps = Lists.newArrayList(); + // Omit ourselves from the list for (ResolveInfo info : infos) { if (!context.getPackageName().equals(info.activityInfo.packageName)) { - mApps.add(info); + apps.add(new AppItem(info)); + } + } + + if (apps.size() > 0) { + add(new SpacerItem()); + for (Item item : apps) { + add(item); } } } + } - final RootComparator comp = new RootComparator(); - mServices.sort(comp); - mShortcuts.sort(comp); - mDevices.sort(comp); + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final Item item = getItem(position); + return item.getView(convertView, parent); + } - if (mRecent.getCount() > 0) { - addSection(mRecent); - } - if (mServices.getCount() > 0) { - addSection(mServices); - } - if (mShortcuts.getCount() > 0) { - addSection(mShortcuts); - } - if (mDevices.getCount() > 0) { - addSection(mDevices); - } - if (mApps.getCount() > 0) { - addSection(mApps); + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return getItemViewType(position) != 1; + } + + @Override + public int getItemViewType(int position) { + final Item item = getItem(position); + if (item instanceof RootItem || item instanceof AppItem) { + return 0; + } else { + return 1; } } + + @Override + public int getViewTypeCount() { + return 2; + } } public static class RootComparator implements Comparator<RootInfo> { diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java index 57fc7e4eb9e5..1a473080fbb3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java @@ -213,8 +213,13 @@ public class TestActivity extends Activity { if (DocumentsContract.isDocumentUri(this, uri)) { result += "; DOC_ID"; } - getContentResolver() - .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + try { + getContentResolver().takePersistableUriPermission( + uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } catch (SecurityException e) { + result += "; FAILED TO TAKE"; + Log.e(TAG, "Failed to take", e); + } InputStream is = null; try { is = getContentResolver().openInputStream(uri); @@ -222,7 +227,7 @@ public class TestActivity extends Activity { result += "; read length=" + length; } catch (Exception e) { result += "; ERROR"; - Log.w(TAG, "Failed to read " + uri, e); + Log.e(TAG, "Failed to read " + uri, e); } finally { IoUtils.closeQuietly(is); } @@ -235,15 +240,20 @@ public class TestActivity extends Activity { if (DocumentsContract.isDocumentUri(this, uri)) { result += "; DOC_ID"; } - getContentResolver() - .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + try { + getContentResolver().takePersistableUriPermission( + uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } catch (SecurityException e) { + result += "; FAILED TO TAKE"; + Log.e(TAG, "Failed to take", e); + } OutputStream os = null; try { os = getContentResolver().openOutputStream(uri); os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes()); } catch (Exception e) { result += "; ERROR"; - Log.w(TAG, "Failed to write " + uri, e); + Log.e(TAG, "Failed to write " + uri, e); } finally { IoUtils.closeQuietly(os); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java index 0a378c07532a..28bab6cf4966 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java @@ -71,6 +71,25 @@ public class DocumentStack extends LinkedList<DocumentInfo> implements Durable { } } + /** + * Build key that uniquely identifies this stack. It omits most of the raw + * details included in {@link #write(DataOutputStream)}, since they change + * too regularly to be used as a key. + */ + public String buildKey() { + final StringBuilder builder = new StringBuilder(); + if (root != null) { + builder.append(root.authority).append('#'); + builder.append(root.rootId).append('#'); + } else { + builder.append("[null]").append('#'); + } + for (DocumentInfo doc : this) { + builder.append(doc.documentId).append('#'); + } + return builder.toString(); + } + @Override public void reset() { clear(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index 014901a2937f..e220c9e382fb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -42,10 +42,10 @@ import java.util.Objects; */ public class RootInfo implements Durable, Parcelable { private static final int VERSION_INIT = 1; + private static final int VERSION_DROP_TYPE = 2; public String authority; public String rootId; - public int rootType; public int flags; public int icon; public String title; @@ -67,7 +67,6 @@ public class RootInfo implements Durable, Parcelable { public void reset() { authority = null; rootId = null; - rootType = 0; flags = 0; icon = 0; title = null; @@ -85,10 +84,9 @@ public class RootInfo implements Durable, Parcelable { public void read(DataInputStream in) throws IOException { final int version = in.readInt(); switch (version) { - case VERSION_INIT: + case VERSION_DROP_TYPE: authority = DurableUtils.readNullableString(in); rootId = DurableUtils.readNullableString(in); - rootType = in.readInt(); flags = in.readInt(); icon = in.readInt(); title = DurableUtils.readNullableString(in); @@ -105,10 +103,9 @@ public class RootInfo implements Durable, Parcelable { @Override public void write(DataOutputStream out) throws IOException { - out.writeInt(VERSION_INIT); + out.writeInt(VERSION_DROP_TYPE); DurableUtils.writeNullableString(out, authority); DurableUtils.writeNullableString(out, rootId); - out.writeInt(rootType); out.writeInt(flags); out.writeInt(icon); DurableUtils.writeNullableString(out, title); @@ -146,7 +143,6 @@ public class RootInfo implements Durable, Parcelable { final RootInfo root = new RootInfo(); root.authority = authority; root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID); - root.rootType = getCursorInt(cursor, Root.COLUMN_ROOT_TYPE); root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS); root.icon = getCursorInt(cursor, Root.COLUMN_ICON); root.title = getCursorString(cursor, Root.COLUMN_TITLE); @@ -162,25 +158,44 @@ public class RootInfo implements Durable, Parcelable { derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null; // TODO: remove these special case icons - if ("com.android.externalstorage.documents".equals(authority)) { - if ("documents".equals(rootId)) { - derivedIcon = R.drawable.ic_doc_text; - } else { - derivedIcon = R.drawable.ic_root_sdcard; - } - } - if ("com.android.providers.downloads.documents".equals(authority)) { + if (isExternalStorage()) { + derivedIcon = R.drawable.ic_root_sdcard; + } else if (isDownloads()) { derivedIcon = R.drawable.ic_root_download; + } else if (isImages()) { + derivedIcon = R.drawable.ic_doc_image; + } else if (isVideos()) { + derivedIcon = R.drawable.ic_doc_video; + } else if (isAudio()) { + derivedIcon = R.drawable.ic_doc_audio; } - if ("com.android.providers.media.documents".equals(authority)) { - if ("images_root".equals(rootId)) { - derivedIcon = R.drawable.ic_doc_image; - } else if ("videos_root".equals(rootId)) { - derivedIcon = R.drawable.ic_doc_video; - } else if ("audio_root".equals(rootId)) { - derivedIcon = R.drawable.ic_doc_audio; - } - } + } + + public boolean isRecents() { + return authority == null && rootId == null; + } + + public boolean isExternalStorage() { + return "com.android.externalstorage.documents".equals(authority); + } + + public boolean isDownloads() { + return "com.android.providers.downloads.documents".equals(authority); + } + + public boolean isImages() { + return "com.android.providers.media.documents".equals(authority) + && "images_root".equals(rootId); + } + + public boolean isVideos() { + return "com.android.providers.media.documents".equals(authority) + && "videos_root".equals(rootId); + } + + public boolean isAudio() { + return "com.android.providers.media.documents".equals(authority) + && "audio_root".equals(rootId); } @Override diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index ed28da5225e9..9328b33a73ae 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -47,9 +47,8 @@ public class ExternalStorageProvider extends DocumentsProvider { // docId format: root:path/to/file private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { - Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON, - Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, - Root.COLUMN_AVAILABLE_BYTES, + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, + Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, }; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { @@ -59,7 +58,6 @@ public class ExternalStorageProvider extends DocumentsProvider { private static class RootInfo { public String rootId; - public int rootType; public int flags; public String title; public String docId; @@ -84,7 +82,6 @@ public class ExternalStorageProvider extends DocumentsProvider { final RootInfo root = new RootInfo(); root.rootId = rootId; - root.rootType = Root.ROOT_TYPE_DEVICE; root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED | Root.FLAG_SUPPORTS_SEARCH; root.title = getContext().getString(R.string.root_internal_storage); @@ -198,7 +195,6 @@ public class ExternalStorageProvider extends DocumentsProvider { final RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, root.rootId); - row.add(Root.COLUMN_ROOT_TYPE, root.rootType); row.add(Root.COLUMN_FLAGS, root.flags); row.add(Root.COLUMN_TITLE, root.title); row.add(Root.COLUMN_DOCUMENT_ID, root.docId); diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java index e6fbb1b762e0..5a15cd2dacdc 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java @@ -65,7 +65,7 @@ public class TestDocumentsProvider extends DocumentsProvider { private static final String MY_DOC_NULL = "myNull"; private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { - Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON, + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, }; @@ -114,7 +114,6 @@ public class TestDocumentsProvider extends DocumentsProvider { final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); final RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID); - row.add(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE); row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS); row.add(Root.COLUMN_TITLE, "_Test title which is really long"); row.add(Root.COLUMN_SUMMARY, diff --git a/packages/Keyguard/res/layout-land/keyguard_host_view.xml b/packages/Keyguard/res/layout-land/keyguard_host_view.xml index 87b8b59fad62..eeb9ee7998c1 100644 --- a/packages/Keyguard/res/layout-land/keyguard_host_view.xml +++ b/packages/Keyguard/res/layout-land/keyguard_host_view.xml @@ -51,6 +51,11 @@ androidprv:layout_maxHeight="480dp" /> <include layout="@layout/keyguard_multi_user_selector"/> + <View android:layout_width="match_parent" + android:layout_height="match_parent" + androidprv:layout_childType="scrim" + android:background="#99000000" /> + <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" diff --git a/packages/Keyguard/res/layout-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-port/keyguard_host_view.xml index 355739e08e5c..8498dcfe3d83 100644 --- a/packages/Keyguard/res/layout-port/keyguard_host_view.xml +++ b/packages/Keyguard/res/layout-port/keyguard_host_view.xml @@ -55,6 +55,11 @@ android:layout_gravity="center"/> </FrameLayout> + <View android:layout_width="match_parent" + android:layout_height="match_parent" + androidprv:layout_childType="scrim" + android:background="#99000000" /> + <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" diff --git a/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml index 42dbe9d9e1bb..77bc9b5bc129 100644 --- a/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml +++ b/packages/Keyguard/res/layout-sw600dp-port/keyguard_host_view.xml @@ -52,6 +52,11 @@ <include layout="@layout/keyguard_multi_user_selector"/> + <View android:layout_width="match_parent" + android:layout_height="match_parent" + androidprv:layout_childType="scrim" + android:background="#99000000" /> + <com.android.keyguard.KeyguardSecurityContainer android:id="@+id/keyguard_security_container" android:layout_width="wrap_content" diff --git a/packages/Keyguard/res/layout/keyguard_transport_control_view.xml b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml index 7e36f9f68851..801999a7cbbf 100644 --- a/packages/Keyguard/res/layout/keyguard_transport_control_view.xml +++ b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml @@ -22,22 +22,16 @@ android:gravity="center_horizontal" android:id="@+id/keyguard_transport_control"> - <!-- FrameLayout used as scrim to show between album art and buttons --> - <FrameLayout + <!-- Use ImageView for its cropping features; otherwise could be android:background --> + <ImageView + android:id="@+id/albumart" android:layout_width="match_parent" android:layout_height="match_parent" - android:foreground="@drawable/ic_lockscreen_player_background" - android:contentDescription="@string/keygaurd_accessibility_media_controls"> - <!-- Use ImageView for its cropping features; otherwise could be android:background --> - <ImageView - android:id="@+id/albumart" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="fill" - android:scaleType="centerCrop" - android:adjustViewBounds="false" - /> - </FrameLayout> + android:layout_gravity="fill" + android:scaleType="centerCrop" + android:adjustViewBounds="false" + android:contentDescription="@string/keygaurd_accessibility_media_controls" /> + <LinearLayout android:orientation="vertical" diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml index 16a3f3f070b4..a31f708601d4 100644 --- a/packages/Keyguard/res/values/styles.xml +++ b/packages/Keyguard/res/values/styles.xml @@ -22,15 +22,16 @@ <style name="Widget.Button.NumPadKey" parent="@android:style/Widget.Button"> <item name="android:singleLine">true</item> - <item name="android:padding">6dip</item> <item name="android:gravity">left|center_vertical</item> <item name="android:background">?android:attr/selectableItemBackground</item> <item name="android:textSize">34dp</item> <item name="android:fontFamily">sans-serif</item> <item name="android:textStyle">normal</item> <item name="android:textColor">#ffffff</item> - <item name="android:paddingBottom">10dp</item> + <item name="android:paddingTop">6dp</item> + <item name="android:paddingBottom">8dp</item> <item name="android:paddingLeft">20dp</item> + <item name="android:paddingRight">6dp</item> </style> <style name="TextAppearance.NumPadKey" parent="@android:style/TextAppearance"> diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index a9e9d3aaa6da..4da6171cb43c 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -123,6 +123,8 @@ public class KeyguardHostView extends KeyguardViewBase { protected boolean mShowSecurityWhenReturn; + private final Rect mInsets = new Rect(); + /*package*/ interface UserSwitcherCallback { void hideSecurityView(int duration); void showSecurityView(); @@ -405,11 +407,6 @@ public class KeyguardHostView extends KeyguardViewBase { updateSecurityViews(); } - public void setScrimView(View scrim) { - if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setScrimView(scrim); - if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setScrimView(scrim); - } - private void setBackButtonEnabled(boolean enabled) { if (mContext instanceof Activity) return; // always enabled in activity mode setSystemUiVisibility(enabled ? @@ -431,10 +428,11 @@ public class KeyguardHostView extends KeyguardViewBase { } private boolean widgetsDisabled() { + boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic(); boolean disabledByDpm = (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0; boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled(); - return disabledByDpm || disabledByUser; + return disabledByLowRamDevice || disabledByDpm || disabledByUser; } private boolean cameraDisabledByDpm() { @@ -1351,6 +1349,7 @@ public class KeyguardHostView extends KeyguardViewBase { static class SavedState extends BaseSavedState { int transportState; int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; + Rect insets = new Rect(); SavedState(Parcelable superState) { super(superState); @@ -1360,6 +1359,7 @@ public class KeyguardHostView extends KeyguardViewBase { super(in); this.transportState = in.readInt(); this.appWidgetToShow = in.readInt(); + this.insets = in.readParcelable(null); } @Override @@ -1367,6 +1367,7 @@ public class KeyguardHostView extends KeyguardViewBase { super.writeToParcel(out, flags); out.writeInt(this.transportState); out.writeInt(this.appWidgetToShow); + out.writeParcelable(insets, 0); } public static final Parcelable.Creator<SavedState> CREATOR @@ -1391,6 +1392,7 @@ public class KeyguardHostView extends KeyguardViewBase { && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0; ss.transportState = showing ? TRANSPORT_VISIBLE : mTransportState; ss.appWidgetToShow = mAppWidgetToShow; + ss.insets.set(mInsets); return ss; } @@ -1404,11 +1406,24 @@ public class KeyguardHostView extends KeyguardViewBase { super.onRestoreInstanceState(ss.getSuperState()); mTransportState = (ss.transportState); mAppWidgetToShow = ss.appWidgetToShow; + setInsets(ss.insets); if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState); post(mSwitchPageRunnable); } @Override + protected boolean fitSystemWindows(Rect insets) { + setInsets(insets); + return true; + } + + private void setInsets(Rect insets) { + mInsets.set(insets); + if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets); + if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets); + } + + @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused")); @@ -1469,6 +1484,7 @@ public class KeyguardHostView extends KeyguardViewBase { if (DEBUGXPORT) Log.v(TAG, "remove transport"); mAppWidgetContainer.removeWidget(getOrCreateTransportControl()); mTransportControl = null; + KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null); } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java index 3208aff56fca..2a5f9790811a 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java @@ -36,6 +36,7 @@ import android.text.Spannable; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.View; @@ -98,11 +99,9 @@ public class KeyguardTransportControlView extends FrameLayout implements OnClick case MSG_SET_ARTWORK: if (mClientGeneration == msg.arg1) { - if (mMetadata.bitmap != null) { - mMetadata.bitmap.recycle(); - } mMetadata.bitmap = (Bitmap) msg.obj; - mAlbumArt.setImageBitmap(mMetadata.bitmap); + KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground( + mMetadata.bitmap); } break; @@ -223,7 +222,8 @@ public class KeyguardTransportControlView extends FrameLayout implements OnClick @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { if (mAttached) { - int dim = Math.min(512, Math.max(w, h)); + final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); + int dim = Math.max(dm.widthPixels, dm.heightPixels); if (DEBUG) Log.v(TAG, "TCV uses bitmap size=" + dim); mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim); } @@ -300,7 +300,8 @@ public class KeyguardTransportControlView extends FrameLayout implements OnClick Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } - mAlbumArt.setImageBitmap(mMetadata.bitmap); + KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground( + mMetadata.bitmap); final int flags = mTransportControlFlags; setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS); setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index f4bbf9adda38..734f51714be6 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -249,11 +249,11 @@ public class KeyguardUpdateMonitor { if (Intent.ACTION_TIME_TICK.equals(action) || Intent.ACTION_TIME_CHANGED.equals(action) || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE)); + mHandler.sendEmptyMessage(MSG_TIME_UPDATE); } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { mTelephonyPlmn = getTelephonyPlmnFrom(intent); mTelephonySpn = getTelephonySpnFrom(intent); - mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE)); + mHandler.sendEmptyMessage(MSG_CARRIER_INFO_UPDATE); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); @@ -277,12 +277,12 @@ public class KeyguardUpdateMonitor { mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED .equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED)); + mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_BOOT_COMPLETED)); + dispatchBootCompleted(); } } }; @@ -407,6 +407,20 @@ public class KeyguardUpdateMonitor { return sInstance; } + /** + * IMPORTANT: Must be called from UI thread. + */ + public void dispatchSetBackground(Bitmap bmp) { + if (DEBUG) Log.d(TAG, "dispatchSetBackground"); + final int count = mCallbacks.size(); + for (int i = 0; i < count; i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onSetBackground(bmp); + } + } + } + protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) { mDisplayClientState.clientGeneration = clientGeneration; mDisplayClientState.clearing = clearing; @@ -520,7 +534,7 @@ public class KeyguardUpdateMonitor { super.onChange(selfChange); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); if (mDeviceProvisioned) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED)); + mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED); } if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned); } @@ -536,7 +550,7 @@ public class KeyguardUpdateMonitor { if (provisioned != mDeviceProvisioned) { mDeviceProvisioned = provisioned; if (mDeviceProvisioned) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED)); + mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED); } } } @@ -582,6 +596,18 @@ public class KeyguardUpdateMonitor { } /** + * This is exposed since {@link Intent#ACTION_BOOT_COMPLETED} is not sticky. If + * keyguard crashes sometime after boot, then it will never receive this + * broadcast and hence not handle the event. This method is ultimately called by + * PhoneWindowManager in this case. + */ + protected void dispatchBootCompleted() { + if (!mBootCompleted) { + mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); + } + } + + /** * Handle {@link #MSG_BOOT_COMPLETED} */ protected void handleBootCompleted() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index b0511e56a6b1..e6dddab148f8 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -17,6 +17,7 @@ package com.android.keyguard; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; +import android.graphics.Bitmap; import android.media.AudioManager; import com.android.internal.telephony.IccCardConstants; @@ -135,4 +136,8 @@ class KeyguardUpdateMonitorCallback { * Called when the emergency call button is pressed. */ void onEmergencyCallAction() { } + + public void onSetBackground(Bitmap bitmap) { + // THIS SPACE FOR RENT + } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java index a0e44d75247c..2084a168cfe7 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java @@ -16,6 +16,9 @@ package com.android.keyguard; +import android.app.PendingIntent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.widget.LockPatternUtils; @@ -77,6 +80,14 @@ public class KeyguardViewManager { private boolean mScreenOn = false; private LockPatternUtils mLockPatternUtils; + private KeyguardUpdateMonitorCallback mBackgroundChanger = new KeyguardUpdateMonitorCallback() { + @Override + public void onSetBackground(Bitmap bmp) { + mKeyguardHost.setCustomBackground(bmp != null ? + new BitmapDrawable(mContext.getResources(), bmp) : null); + } + }; + public interface ShowListener { void onShown(IBinder windowToken); }; @@ -139,11 +150,26 @@ public class KeyguardViewManager { class ViewManagerHost extends FrameLayout { private static final int BACKGROUND_COLOR = 0x70000000; + + private Drawable mCustomBackground; + // This is a faster way to draw the background on devices without hardware acceleration private final Drawable mBackgroundDrawable = new Drawable() { @Override public void draw(Canvas canvas) { - canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC); + if (mCustomBackground != null) { + final Rect bounds = mCustomBackground.getBounds(); + final int vWidth = getWidth(); + final int vHeight = getHeight(); + + final int restore = canvas.save(); + canvas.translate(-(bounds.width() - vWidth) / 2, + -(bounds.height() - vHeight) / 2); + mCustomBackground.draw(canvas); + canvas.restoreToCount(restore); + } else { + canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC); + } } @Override @@ -159,54 +185,44 @@ public class KeyguardViewManager { return PixelFormat.TRANSLUCENT; } }; - private final View mScrimView; - private boolean mExtendIntoPadding; - public ViewManagerHost(Context context, boolean extendIntoPadding) { + + public ViewManagerHost(Context context) { super(context); - mExtendIntoPadding = extendIntoPadding; - setFitsSystemWindows(true); - setClipToPadding(!mExtendIntoPadding); setBackground(mBackgroundDrawable); - - mScrimView = new View(context); - mScrimView.setVisibility(View.GONE); - mScrimView.setBackgroundColor(0x99000000); - addView(mScrimView); } - private boolean considerPadding(View child) { - return !mExtendIntoPadding || child instanceof KeyguardHostView; + public void setCustomBackground(Drawable d) { + mCustomBackground = d; + if (d != null) { + d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER); + } + computeCustomBackgroundBounds(); + invalidate(); } - @Override - protected void measureChildWithMargins(View child, - int parentWidthMeasureSpec, int widthUsed, - int parentHeightMeasureSpec, int heightUsed) { - if (considerPadding(child)) { - // don't extend into padding (default behavior) - super.measureChildWithMargins(child, - parentWidthMeasureSpec, widthUsed, - parentHeightMeasureSpec, heightUsed); + private void computeCustomBackgroundBounds() { + if (mCustomBackground == null) return; // Nothing to do + if (!isLaidOut()) return; // We'll do this later + + final int bgWidth = mCustomBackground.getIntrinsicWidth(); + final int bgHeight = mCustomBackground.getIntrinsicHeight(); + final int vWidth = getWidth(); + final int vHeight = getHeight(); + + final float bgAspect = (float) bgWidth / bgHeight; + final float vAspect = (float) vWidth / vHeight; + + if (bgAspect > vAspect) { + mCustomBackground.setBounds(0, 0, (int) (vHeight * bgAspect), vHeight); } else { - // allowed to extend into padding (scrim / camera preview) - child.measure(parentWidthMeasureSpec, parentHeightMeasureSpec); + mCustomBackground.setBounds(0, 0, vWidth, (int) (vWidth / bgAspect)); } } @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - int cl = l, ct = t, cr = r, cb = b; - if (considerPadding(child)) { - cl += mPaddingLeft; - ct += mPaddingTop; - cr -= mPaddingRight; - cb -= mPaddingBottom; - } - child.layout(cl, ct, cr, cb); - } + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + computeCustomBackgroundBounds(); } @Override @@ -252,7 +268,7 @@ public class KeyguardViewManager { if (mKeyguardHost == null) { if (DEBUG) Log.d(TAG, "keyguard host is null, creating it..."); - mKeyguardHost = new ViewManagerHost(mContext, shouldEnableTransparentBars()); + mKeyguardHost = new ViewManagerHost(mContext); int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR @@ -282,9 +298,13 @@ public class KeyguardViewManager { lp.setTitle("Keyguard"); mWindowLayoutParams = lp; mViewManager.addView(mKeyguardHost, lp); + + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger); } if (force || mKeyguardView == null) { + mKeyguardHost.setCustomBackground(null); + mKeyguardHost.removeAllViews(); inflateKeyguardView(options); mKeyguardView.requestFocus(); } @@ -306,7 +326,6 @@ public class KeyguardViewManager { mKeyguardView.setViewMediatorCallback(mViewMediatorCallback); mKeyguardView.initializeSwitchingUserState(options != null && options.getBoolean(IS_SWITCHING_USER)); - mKeyguardView.setScrimView(mKeyguardHost.mScrimView); // HACK // The keyguard view will have set up window flags in onFinishInflate before we set @@ -471,6 +490,8 @@ public class KeyguardViewManager { public void run() { synchronized (KeyguardViewManager.this) { lastView.cleanUp(); + // Let go of any large bitmaps. + mKeyguardHost.setCustomBackground(null); mKeyguardHost.removeView(lastView); } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java index 0606d838d915..ec3eb157d629 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.graphics.Bitmap; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardShowCallback; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; @@ -528,6 +529,9 @@ public class KeyguardViewMediator { mSystemReady = true; mUpdateMonitor.registerCallback(mUpdateCallback); + // Send boot completed message if it hasn't already been sent. + mUpdateMonitor.dispatchBootCompleted(); + // Suppress biometric unlock right after boot until things have settled if it is the // selected security method, otherwise unsuppress it. It must be unsuppressed if it is // not the selected security method for the following reason: if the user starts diff --git a/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java index 76a7fe3fa510..67d0d5a67d71 100644 --- a/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java +++ b/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java @@ -28,6 +28,7 @@ import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.View.MeasureSpec; import android.widget.LinearLayout; public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout { @@ -47,6 +48,7 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo private final Rect mTempRect = new Rect(); private final Rect mZeroPadding = new Rect(); + private final Rect mInsets = new Rect(); private final DisplayMetrics mDisplayMetrics; @@ -80,6 +82,10 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE); } + public void setInsets(Rect insets) { + mInsets.set(insets); + } + @Override public boolean isChallengeShowing() { return true; @@ -187,7 +193,7 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo // This calculation is super dodgy and relies on several assumptions. // Specifically that the root of the window will be padded in for insets // and that the window is LAYOUT_IN_SCREEN. - virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop(); + virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop() - mInsets.top; } if (lp.childType == LayoutParams.CHILD_TYPE_WIDGET || lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) { @@ -213,6 +219,9 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo final int height = MeasureSpec.getSize(heightSpec); setMeasuredDimension(width, height); + final int insetHeight = height - mInsets.top - mInsets.bottom; + final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY); + int widthUsed = 0; int heightUsed = 0; @@ -245,14 +254,14 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo if (child.getVisibility() == GONE) continue; int adjustedWidthSpec = widthSpec; - int adjustedHeightSpec = heightSpec; + int adjustedHeightSpec = insetHeightSpec; if (lp.maxWidth >= 0) { adjustedWidthSpec = MeasureSpec.makeMeasureSpec( Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY); } if (lp.maxHeight >= 0) { adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - Math.min(lp.maxHeight, height), MeasureSpec.EXACTLY); + Math.min(lp.maxHeight, insetHeight), MeasureSpec.EXACTLY); } // measureChildWithMargins will resolve layout direction for the LayoutParams measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0); @@ -282,7 +291,7 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo continue; } - final int virtualHeight = getVirtualHeight(lp, height, heightUsed); + final int virtualHeight = getVirtualHeight(lp, insetHeight, heightUsed); int adjustedWidthSpec; int adjustedHeightSpec; @@ -330,11 +339,12 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo padding.bottom = getPaddingBottom(); final int width = r - l; final int height = b - t; + final int insetHeight = height - mInsets.top - mInsets.bottom; // Reserve extra space in layout for the user switcher by modifying // local padding during this layout pass if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) { - layoutWithGravity(width, height, mUserSwitcherView, padding, true); + layoutWithGravity(width, insetHeight, mUserSwitcherView, padding, true); } final int count = getChildCount(); @@ -349,11 +359,11 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo child.layout(0, 0, width, height); continue; } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) { - layoutWithGravity(width, height, child, mZeroPadding, false); + layoutWithGravity(width, insetHeight, child, mZeroPadding, false); continue; } - layoutWithGravity(width, height, child, padding, false); + layoutWithGravity(width, insetHeight, child, padding, false); } } @@ -445,6 +455,8 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo right = left + childWidth; break; } + top += mInsets.top; + bottom += mInsets.top; child.layout(left, top, right, bottom); } diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java index 4a4e7fa907e5..2e47768220d8 100644 --- a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java +++ b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java @@ -24,6 +24,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.FloatProperty; @@ -125,6 +126,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout private ObjectAnimator mFrameAnimation; private boolean mHasGlowpad; + private final Rect mInsets = new Rect(); // We have an internal and external version, and we and them together. private boolean mChallengeInteractiveExternal = true; @@ -261,6 +263,10 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE); } + public void setInsets(Rect insets) { + mInsets.set(insets); + } + public void setHandleAlpha(float alpha) { if (mExpandChallengeView != null) { mExpandChallengeView.setAlpha(alpha); @@ -797,11 +803,13 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout throw new IllegalArgumentException( "SlidingChallengeLayout must be measured with an exact size"); } - final int width = MeasureSpec.getSize(widthSpec); final int height = MeasureSpec.getSize(heightSpec); setMeasuredDimension(width, height); + final int insetHeight = height - mInsets.top - mInsets.bottom; + final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY); + // Find one and only one challenge view. final View oldChallengeView = mChallengeView; final View oldExpandChallengeView = mChallengeView; @@ -861,13 +869,13 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // We base this on the layout_maxHeight on the challenge view. If it comes out // negative or zero, either we didn't have a maxHeight or we're totally out of space, // so give up and measure as if this rule weren't there. - int challengeHeightSpec = heightSpec; + int challengeHeightSpec = insetHeightSpec; final View root = getRootView(); if (root != null) { final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams(); - final int specSize = MeasureSpec.getSize(heightSpec); - final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop(); - final int diff = windowHeight - specSize; + final int windowHeight = mDisplayMetrics.heightPixels + - root.getPaddingTop() - mInsets.top; + final int diff = windowHeight - insetHeight; final int maxChallengeHeight = lp.maxHeight - diff; if (maxChallengeHeight > 0) { challengeHeightSpec = makeChildMeasureSpec(maxChallengeHeight, lp.height); @@ -887,7 +895,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // Measure children. Widget frame measures special, so that we can ignore // insets for the IME. - int parentWidthSpec = widthSpec, parentHeightSpec = heightSpec; + int parentWidthSpec = widthSpec, parentHeightSpec = insetHeightSpec; final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { final View root = getRootView(); @@ -896,12 +904,17 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // Specifically that the root of the window will be padded in for insets // and that the window is LAYOUT_IN_SCREEN. final int windowWidth = mDisplayMetrics.widthPixels; - final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop(); + final int windowHeight = mDisplayMetrics.heightPixels + - root.getPaddingTop() - mInsets.top; parentWidthSpec = MeasureSpec.makeMeasureSpec( windowWidth, MeasureSpec.EXACTLY); parentHeightSpec = MeasureSpec.makeMeasureSpec( windowHeight, MeasureSpec.EXACTLY); } + } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { + // Allow scrim views to extend into the insets + parentWidthSpec = widthSpec; + parentHeightSpec = heightSpec; } measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0); } @@ -931,7 +944,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); final int left = center - childWidth / 2; - final int layoutBottom = height - paddingBottom - lp.bottomMargin; + final int layoutBottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; // We use the top of the challenge view to position the handle, so // we never want less than the handle size showing at the bottom. final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound) @@ -942,15 +955,18 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout final int center = (paddingLeft + width - paddingRight) / 2; final int left = center - child.getMeasuredWidth() / 2; final int right = left + child.getMeasuredWidth(); - final int bottom = height - paddingBottom - lp.bottomMargin; + final int bottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; final int top = bottom - child.getMeasuredHeight(); child.layout(left, top, right, bottom); + } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { + // Scrim views use the entire area, including padding & insets + child.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); } else { // Non-challenge views lay out from the upper left, layered. child.layout(paddingLeft + lp.leftMargin, - paddingTop + lp.topMargin, + paddingTop + lp.topMargin + mInsets.top, paddingLeft + child.getMeasuredWidth(), - paddingTop + child.getMeasuredHeight()); + paddingTop + child.getMeasuredHeight() + mInsets.top); } } @@ -1076,7 +1092,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout final int layoutBottom = getLayoutBottom(); final int challengeHeight = mChallengeView.getMeasuredHeight(); - return layoutBottom - challengeHeight; + return layoutBottom - challengeHeight - mInsets.top; } /** @@ -1125,7 +1141,8 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout final int bottomMargin = (mChallengeView == null) ? 0 : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin; - final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin; + final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin + - mInsets.bottom; return layoutBottom; } diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk index f65fe4b7c64b..9e7b969ce1b3 100644 --- a/packages/PrintSpooler/Android.mk +++ b/packages/PrintSpooler/Android.mk @@ -24,7 +24,4 @@ LOCAL_PACKAGE_NAME := PrintSpooler LOCAL_JAVA_LIBRARIES := framework-base -LOCAL_PROGUARD_ENABLED := disabled - include $(BUILD_PACKAGE) - diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml new file mode 100644 index 000000000000..6439b49acc9d --- /dev/null +++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:orientation="horizontal" + android:gravity="start|center_vertical"> + + <ImageView + android:id="@+id/icon" + android:layout_width="32dip" + android:layout_height="32dip" + android:layout_gravity="center_vertical" + android:layout_marginEnd="8dip" + android:duplicateParentState="true" + android:contentDescription="@null" + android:visibility="gone"> + </ImageView> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:singleLine="true" + android:ellipsize="end" + android:textIsSelectable="false" + android:gravity="top|start" + android:textColor="@color/item_text_color" + android:duplicateParentState="true"> + </TextView> + + <TextView + android:id="@+id/subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:singleLine="true" + android:ellipsize="end" + android:textIsSelectable="false" + android:visibility="gone" + android:textColor="@color/print_option_title" + android:duplicateParentState="true"> + </TextView> + + </LinearLayout> + +</LinearLayout> diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 9fe7e00e7d09..5ee8d8cb67c8 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -109,6 +109,9 @@ <!-- Label for an unknown reason for failed or blocked print job. [CHAR LIMIT=25] --> <string name="reason_unknown">unknown</string> + <!-- Label for a printer that is not available. [CHAR LIMIT=25] --> + <string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> – unavailable</string> + <!-- Arrays --> <!-- Color mode labels. --> diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java index 3a1a3c40662c..65af8304d249 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java @@ -19,13 +19,16 @@ package com.android.printspooler; import android.content.ComponentName; import android.content.Context; import android.content.Loader; +import android.content.pm.ServiceInfo; import android.os.AsyncTask; import android.print.PrintManager; import android.print.PrinterDiscoverySession; import android.print.PrinterDiscoverySession.OnPrintersChangeListener; import android.print.PrinterId; import android.print.PrinterInfo; +import android.printservice.PrintServiceInfo; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; @@ -46,6 +49,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import libcore.io.IoUtils; @@ -384,6 +388,30 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { + FusedPrintersProvider.this.hashCode()); } + // Ignore printer records whose target services are not installed. + PrintManager printManager = (PrintManager) getContext() + .getSystemService(Context.PRINT_SERVICE); + List<PrintServiceInfo> services = printManager + .getInstalledPrintServices(); + + Set<ComponentName> installedComponents = new ArraySet<ComponentName>(); + final int installedServiceCount = services.size(); + for (int i = 0; i < installedServiceCount; i++) { + ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo; + ComponentName componentName = new ComponentName( + serviceInfo.packageName, serviceInfo.name); + installedComponents.add(componentName); + } + + final int printerCount = printers.size(); + for (int i = printerCount - 1; i >= 0; i--) { + ComponentName printerServiceName = printers.get(i).getId().getServiceName(); + if (!installedComponents.contains(printerServiceName.getPackageName())) { + printers.remove(i); + } + } + + // Store the filtered list. mHistoricalPrinters = printers; // Compute the favorite printers. diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 8d11a9392d0f..44362d4ab85f 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -75,11 +75,10 @@ import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.EditText; +import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; -import libcore.io.IoUtils; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -96,6 +95,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import libcore.io.IoUtils; + /** * Activity for configuring a print job. */ @@ -818,8 +819,6 @@ public class PrintJobConfigActivity extends Activity { private PrinterInfo mCurrentPrinter; - private boolean mRequestedCurrentPrinterRefresh; - private final OnItemSelectedListener mOnItemSelectedListener = new AdapterView.OnItemSelectedListener() { @Override @@ -839,7 +838,7 @@ public class PrintJobConfigActivity extends Activity { return; } - mRequestedCurrentPrinterRefresh = false; + mCapabilitiesTimeout.remove(); mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter .getItem(position); @@ -854,8 +853,7 @@ public class PrintJobConfigActivity extends Activity { PrinterCapabilitiesInfo capabilities = mCurrentPrinter.getCapabilities(); if (capabilities == null) { - // TODO: We need a timeout for the update. - mRequestedCurrentPrinterRefresh = true; + mCapabilitiesTimeout.post(); updateUi(); refreshCurrentPrinter(); } else { @@ -1128,6 +1126,9 @@ public class PrintJobConfigActivity extends Activity { } }; + private final WaitForPrinterCapabilitiesTimeout mCapabilitiesTimeout = + new WaitForPrinterCapabilitiesTimeout(); + private int mEditorState; private boolean mIgnoreNextDestinationChange; @@ -1173,16 +1174,16 @@ public class PrintJobConfigActivity extends Activity { if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE && printer.getCapabilities() == null - && !mRequestedCurrentPrinterRefresh) { - mRequestedCurrentPrinterRefresh = true; + && !mCapabilitiesTimeout.isPosted()) { + mCapabilitiesTimeout.post(); refreshCurrentPrinter(); return; } // We just refreshed the current printer. if (printer.getCapabilities() != null - && mRequestedCurrentPrinterRefresh) { - mRequestedCurrentPrinterRefresh = false; + && mCapabilitiesTimeout.isPosted()) { + mCapabilitiesTimeout.remove(); updatePrintAttributes(printer.getCapabilities()); updateUi(); mController.update(); @@ -1971,6 +1972,43 @@ public class PrintJobConfigActivity extends Activity { } } + private final class WaitForPrinterCapabilitiesTimeout implements Runnable { + private static final long GET_CAPABILITIES_TIMEOUT_MILLIS = 10000; // 10sec + + private boolean mIsPosted; + + public void post() { + if (!mIsPosted) { + mDestinationSpinner.postDelayed(this, + GET_CAPABILITIES_TIMEOUT_MILLIS); + mIsPosted = true; + } + } + + public void remove() { + if (mIsPosted) { + mIsPosted = false; + mDestinationSpinner.removeCallbacks(this); + } + } + + public boolean isPosted() { + return mIsPosted; + } + + @Override + public void run() { + mIsPosted = false; + if (mDestinationSpinner.getSelectedItemPosition() >= 0) { + View itemView = mDestinationSpinner.getSelectedView(); + TextView titleView = (TextView) itemView.findViewById(R.id.title); + String title = getString(R.string.printer_unavailable, + mCurrentPrinter.getName()); + titleView.setText(title); + } + } + } + private final class DestinationAdapter extends BaseAdapter implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{ private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>(); @@ -2066,11 +2104,12 @@ public class PrintJobConfigActivity extends Activity { public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = getLayoutInflater().inflate( - R.layout.spinner_dropdown_item, parent, false); + R.layout.printer_dropdown_item, parent, false); } CharSequence title = null; CharSequence subtitle = null; + Drawable icon = null; if (mPrinters.isEmpty()) { if (position == 0) { @@ -2092,6 +2131,7 @@ public class PrintJobConfigActivity extends Activity { PackageInfo packageInfo = getPackageManager().getPackageInfo( printer.getId().getServiceName().getPackageName(), 0); subtitle = packageInfo.applicationInfo.loadLabel(getPackageManager()); + icon = packageInfo.applicationInfo.loadIcon(getPackageManager()); } catch (NameNotFoundException nnfe) { /* ignore */ } @@ -2110,6 +2150,14 @@ public class PrintJobConfigActivity extends Activity { subtitleView.setVisibility(View.GONE); } + ImageView iconView = (ImageView) convertView.findViewById(R.id.icon); + if (icon != null) { + iconView.setImageDrawable(icon); + iconView.setVisibility(View.VISIBLE); + } else { + iconView.setVisibility(View.GONE); + } + return convertView; } diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index 62b35feb9dda..87181f74ed0e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -43,6 +43,7 @@ import android.print.PrintManager; import android.print.PrinterId; import android.print.PrinterInfo; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; @@ -59,10 +60,12 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -82,6 +85,8 @@ public final class PrintSpoolerService extends Service { private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000; + private static final String PRINT_JOB_FILE_PREFIX = "print_job_"; + private static final String PRINT_FILE_EXTENSION = "pdf"; private static final Object sLock = new Object(); @@ -168,9 +173,9 @@ public final class PrintSpoolerService extends Service { PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); - Message message = mHandlerCaller.obtainMessageIIO( + Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, - printJob.getAppId(), 0, printJob.getId()); + printJob); mHandlerCaller.executeOrSendMessage(message); message = mHandlerCaller.obtainMessageOO( @@ -179,9 +184,6 @@ public final class PrintSpoolerService extends Service { mHandlerCaller.executeOrSendMessage(message); printJob.setCreationTime(System.currentTimeMillis()); - synchronized (mLock) { - mPersistanceManager.writeStateLocked(); - } } @Override @@ -225,12 +227,40 @@ public final class PrintSpoolerService extends Service { } @Override - public void forgetPrintJobs(List<PrintJobId> printJobIds) { - PrintSpoolerService.this.forgetPrintJobs(printJobIds); + protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + PrintSpoolerService.this.dump(fd, writer, args); } }; } + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mLock) { + String prefix = args[0]; + String tab = " "; + + pw.append(prefix).append("print jobs:").println(); + final int printJobCount = mPrintJobs.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo printJob = mPrintJobs.get(i); + pw.append(prefix).append(tab).append(printJob.toString()); + pw.println(); + } + + pw.append(prefix).append("print job files:").println(); + File[] files = getFilesDir().listFiles(); + if (files != null) { + final int fileCount = files.length; + for (int i = 0; i < fileCount; i++) { + File file = files[i]; + if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { + pw.append(prefix).append(tab).append(file.getName()).println(); + } + } + } + } + } + private void sendOnPrintJobQueued(PrintJobInfo printJob) { Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob); @@ -324,10 +354,9 @@ public final class PrintSpoolerService extends Service { case MSG_ON_PRINT_JOB_STATE_CHANGED: { if (mClient != null) { - PrintJobId printJobId = (PrintJobId) message.obj; - final int appId = message.arg1; + PrintJobInfo printJob = (PrintJobInfo) message.obj; try { - mClient.onPrintJobStateChanged(printJobId, appId); + mClient.onPrintJobStateChanged(printJob); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error notify for print job state change.", re); } @@ -391,17 +420,46 @@ public final class PrintSpoolerService extends Service { public void createPrintJob(PrintJobInfo printJob) { synchronized (mLock) { addPrintJobLocked(printJob); + setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null); } } private void handleReadPrintJobsLocked() { + // Make a map with the files for a print job since we may have + // to delete some. One example of getting orphan files if the + // spooler crashes while constructing a print job. We do not + // persist partially populated print jobs under construction to + // avoid special handling for various attributes missing. + ArrayMap<PrintJobId, File> fileForJobMap = null; + File[] files = getFilesDir().listFiles(); + if (files != null) { + final int fileCount = files.length; + for (int i = 0; i < fileCount; i++) { + File file = files[i]; + if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { + if (fileForJobMap == null) { + fileForJobMap = new ArrayMap<PrintJobId, File>(); + } + String printJobIdString = file.getName().substring(0, + PRINT_JOB_FILE_PREFIX.length()); + PrintJobId printJobId = PrintJobId.unflattenFromString( + printJobIdString); + fileForJobMap.put(printJobId, file); + } + } + } + final int printJobCount = mPrintJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); + // We want to have only the orphan files at the end. + if (fileForJobMap != null) { + fileForJobMap.remove(printJob.getId()); + } + // Update the notification. mNotificationController.onPrintJobStateChanged(printJob); - switch (printJob.getState()) { case PrintJobInfo.STATE_QUEUED: case PrintJobInfo.STATE_STARTED: @@ -415,6 +473,15 @@ public final class PrintSpoolerService extends Service { } break; } } + + // Delete the orphan files. + if (fileForJobMap != null) { + final int orphanFileCount = fileForJobMap.size(); + for (int i = 0; i < orphanFileCount; i++) { + File file = fileForJobMap.valueAt(i); + file.delete(); + } + } } public void checkAllPrintJobsHandled() { @@ -465,7 +532,7 @@ public final class PrintSpoolerService extends Service { } public File generateFileForPrintJob(PrintJobId printJobId) { - return new File(getFilesDir(), "print_job_" + return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION); } @@ -476,31 +543,6 @@ public final class PrintSpoolerService extends Service { } } - private void forgetPrintJobs(List<PrintJobId> printJobIds) { - synchronized (mLock) { - boolean printJobsRemoved = false; - final int removedPrintJobCount = printJobIds.size(); - for (int i = 0; i < removedPrintJobCount; i++) { - PrintJobId removedPrintJobId = printJobIds.get(i); - final int printJobCount = mPrintJobs.size(); - for (int j = printJobCount - 1; j >= 0; j--) { - PrintJobInfo printJob = mPrintJobs.get(j); - if (removedPrintJobId.equals(printJob.getId())) { - mPrintJobs.remove(j); - printJobsRemoved = true; - if (DEBUG_PRINT_JOB_LIFECYCLE) { - Slog.i(LOG_TAG, "[FORGOT] " + printJob.getId().flattenToString()); - } - removePrintJobFileLocked(printJob.getId()); - } - } - } - if (printJobsRemoved) { - mPersistanceManager.writeStateLocked(); - } - } - } - private void removeObsoletePrintJobs() { synchronized (mLock) { final int printJobCount = mPrintJobs.size(); @@ -523,7 +565,7 @@ public final class PrintSpoolerService extends Service { if (file.exists()) { file.delete(); if (DEBUG_PRINT_JOB_LIFECYCLE) { - Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId.flattenToString()); + Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId); } } } @@ -552,10 +594,7 @@ public final class PrintSpoolerService extends Service { switch (state) { case PrintJobInfo.STATE_COMPLETED: case PrintJobInfo.STATE_CANCELED: - // Just remove the file but keep the print job info since - // the app that created it may be holding onto the PrintJob - // instance and query it for its most recent state. We will - // remove the info for this job when told so by the system. + mPrintJobs.remove(printJob); removePrintJobFileLocked(printJob.getId()); // $fall-through$ @@ -582,9 +621,9 @@ public final class PrintSpoolerService extends Service { notifyOnAllPrintJobsHandled(); } - Message message = mHandlerCaller.obtainMessageIIO( + Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, - printJob.getAppId(), 0, printJob.getId()); + printJob); mHandlerCaller.executeOrSendMessage(message); } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fb73d397f970..260a3be67191 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -197,9 +197,10 @@ android:name=".DessertCase" android:exported="true" android:label="@string/dessert_case" - android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen" + android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" android:hardwareAccelerated="true" android:launchMode="singleInstance" + android:configChanges="orientation|screenSize" android:excludeFromRecents="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_android.png b/packages/SystemUI/res/drawable-nodpi/dessert_android.png Binary files differnew file mode 100644 index 000000000000..2b47c190016e --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_android.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_cupcake.png b/packages/SystemUI/res/drawable-nodpi/dessert_cupcake.png Binary files differnew file mode 100644 index 000000000000..7b48c108fce2 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_cupcake.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_dandroid.png b/packages/SystemUI/res/drawable-nodpi/dessert_dandroid.png Binary files differnew file mode 100644 index 000000000000..8be85c57c046 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_dandroid.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_donut.png b/packages/SystemUI/res/drawable-nodpi/dessert_donut.png Binary files differnew file mode 100644 index 000000000000..167ced751204 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_donut.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_donutburger.png b/packages/SystemUI/res/drawable-nodpi/dessert_donutburger.png Binary files differnew file mode 100644 index 000000000000..9d77518a81ae --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_donutburger.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_eclair.png b/packages/SystemUI/res/drawable-nodpi/dessert_eclair.png Binary files differnew file mode 100644 index 000000000000..8d463eb1edbd --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_eclair.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_flan.png b/packages/SystemUI/res/drawable-nodpi/dessert_flan.png Binary files differnew file mode 100644 index 000000000000..d05e3de0ce3e --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_flan.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_froyo.png b/packages/SystemUI/res/drawable-nodpi/dessert_froyo.png Binary files differnew file mode 100644 index 000000000000..ffd9994fec32 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_froyo.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_gingerbread.png b/packages/SystemUI/res/drawable-nodpi/dessert_gingerbread.png Binary files differnew file mode 100644 index 000000000000..22bffbb751ba --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_gingerbread.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_honeycomb.png b/packages/SystemUI/res/drawable-nodpi/dessert_honeycomb.png Binary files differnew file mode 100644 index 000000000000..0f51a43e8451 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_honeycomb.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_ics.png b/packages/SystemUI/res/drawable-nodpi/dessert_ics.png Binary files differnew file mode 100644 index 000000000000..bdec60e7f1d1 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_ics.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_jandycane.png b/packages/SystemUI/res/drawable-nodpi/dessert_jandycane.png Binary files differnew file mode 100644 index 000000000000..ba1c7eb7de39 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_jandycane.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_jellybean.png b/packages/SystemUI/res/drawable-nodpi/dessert_jellybean.png Binary files differnew file mode 100644 index 000000000000..5a2bcaa76f91 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_jellybean.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_keylimepie.png b/packages/SystemUI/res/drawable-nodpi/dessert_keylimepie.png Binary files differnew file mode 100644 index 000000000000..a8741ec46083 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_keylimepie.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_kitkat.png b/packages/SystemUI/res/drawable-nodpi/dessert_kitkat.png Binary files differnew file mode 100644 index 000000000000..4f2b03b54e38 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_kitkat.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_petitfour.png b/packages/SystemUI/res/drawable-nodpi/dessert_petitfour.png Binary files differnew file mode 100644 index 000000000000..3dc9d950ccdb --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_petitfour.png diff --git a/packages/SystemUI/res/drawable-nodpi/dessert_zombiegingerbread.png b/packages/SystemUI/res/drawable-nodpi/dessert_zombiegingerbread.png Binary files differnew file mode 100644 index 000000000000..7962c217298f --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/dessert_zombiegingerbread.png diff --git a/packages/SystemUI/res/drawable-nodpi/jandycane.png b/packages/SystemUI/res/drawable-nodpi/jandycane.png Binary files differdeleted file mode 100644 index 278cfec49546..000000000000 --- a/packages/SystemUI/res/drawable-nodpi/jandycane.png +++ /dev/null diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f5356a89867f..cc78cb4f5b41 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -217,4 +217,7 @@ <!-- The width of the notification panel window: match_parent below sw600dp --> <dimen name="notification_panel_width">-1dp</dimen> + + <!-- used by DessertCase --> + <dimen name="dessert_case_cell_size">192dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/DessertCase.java b/packages/SystemUI/src/com/android/systemui/DessertCase.java index b6424af03f77..dd4c0182fe66 100644 --- a/packages/SystemUI/src/com/android/systemui/DessertCase.java +++ b/packages/SystemUI/src/com/android/systemui/DessertCase.java @@ -16,22 +16,51 @@ package com.android.systemui; +import android.animation.ObjectAnimator; import android.app.Activity; import android.content.ComponentName; import android.content.pm.PackageManager; +import android.os.Handler; import android.util.Slog; +import android.view.animation.DecelerateInterpolator; public class DessertCase extends Activity { + DessertCaseView mView; @Override public void onStart() { super.onStart(); - Slog.v("DessertCase", "ACHIEVEMENT UNLOCKED"); PackageManager pm = getPackageManager(); - pm.setComponentEnabledSetting(new ComponentName(this, DessertCaseDream.class), - PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + final ComponentName cn = new ComponentName(this, DessertCaseDream.class); + if (pm.getComponentEnabledSetting(cn) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + Slog.v("DessertCase", "ACHIEVEMENT UNLOCKED"); + pm.setComponentEnabledSetting(cn, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + } - finish(); + mView = new DessertCaseView(this); + + DessertCaseView.RescalingContainer container = new DessertCaseView.RescalingContainer(this); + + container.setView(mView); + + setContentView(container); + } + + @Override + public void onResume() { + super.onResume(); + mView.postDelayed(new Runnable() { + public void run() { + mView.start(); + } + }, 1000); + } + + @Override + public void onPause() { + super.onPause(); + mView.stop(); } } diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java b/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java index 022e4d83a194..a627cf658136 100644 --- a/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java +++ b/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java @@ -19,21 +19,36 @@ package com.android.systemui; import android.service.dreams.DreamService; public class DessertCaseDream extends DreamService { + private DessertCaseView mView; + private DessertCaseView.RescalingContainer mContainer; @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - setInteractive(true); - setFullscreen(true); + setInteractive(false); + + mView = new DessertCaseView(this); + + mContainer = new DessertCaseView.RescalingContainer(this); + + mContainer.setView(mView); + + setContentView(mContainer); } @Override public void onDreamingStarted() { super.onDreamingStarted(); + mView.postDelayed(new Runnable() { + public void run() { + mView.start(); + } + }, 1000); } @Override public void onDreamingStopped() { super.onDreamingStopped(); + mView.stop(); } } diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java new file mode 100644 index 000000000000..99c59d50b651 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.*; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.AnticipateOvershootInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.util.HashSet; +import java.util.Set; + +public class DessertCaseView extends FrameLayout { + private static final String TAG = DessertCaseView.class.getSimpleName(); + + private static final boolean DEBUG = false; + + static final int START_DELAY = 5000; + static final int DELAY = 2000; + static final int DURATION = 500; + + private static final int TAG_POS = 0x2000001; + private static final int TAG_SPAN = 0x2000002; + + private static final int[] PASTRIES = { + R.drawable.dessert_kitkat, // used with permission + R.drawable.dessert_android, // thx irina + }; + + private static final int[] RARE_PASTRIES = { + R.drawable.dessert_cupcake, // 2009 + R.drawable.dessert_donut, // 2009 + R.drawable.dessert_eclair, // 2009 + R.drawable.dessert_froyo, // 2010 + R.drawable.dessert_gingerbread, // 2010 + R.drawable.dessert_honeycomb, // 2011 + R.drawable.dessert_ics, // 2011 + R.drawable.dessert_jellybean, // 2012 + }; + + private static final int[] XRARE_PASTRIES = { + R.drawable.dessert_petitfour, // the original and still delicious + + R.drawable.dessert_donutburger, // remember kids, this was long before cronuts + + R.drawable.dessert_flan, // sholes final approach + // landing gear punted to flan + // runway foam glistens + // -- mcleron + + R.drawable.dessert_keylimepie, // from an alternative timeline + }; + private static final int[] XXRARE_PASTRIES = { + R.drawable.dessert_zombiegingerbread, // thx hackbod + R.drawable.dessert_dandroid, // thx morrildl + R.drawable.dessert_jandycane, // thx nes + }; + + private static final int NUM_PASTRIES = PASTRIES.length + RARE_PASTRIES.length + + XRARE_PASTRIES.length + XXRARE_PASTRIES.length; + + private SparseArray<Drawable> mDrawables = new SparseArray<Drawable>(NUM_PASTRIES); + + private static final float[] MASK = { + 0f, 0f, 0f, 0f, 255f, + 0f, 0f, 0f, 0f, 255f, + 0f, 0f, 0f, 0f, 255f, + 1f, 0f, 0f, 0f, 0f + }; + + private static final float[] WHITE_MASK = { + 0f, 0f, 0f, 0f, 255f, + 0f, 0f, 0f, 0f, 255f, + 0f, 0f, 0f, 0f, 255f, + -1f, 0f, 0f, 0f, 255f + }; + + public static final float SCALE = 0.25f; // natural display size will be SCALE*mCellSize + + private static final float PROB_2X = 0.33f; + private static final float PROB_3X = 0.1f; + private static final float PROB_4X = 0.01f; + + private boolean mStarted; + + private int mCellSize; + private int mWidth, mHeight; + private int mRows, mColumns; + private View[] mCells; + + private final Set<Point> mFreeList = new HashSet<Point>(); + + private final Handler mHandler = new Handler(); + + private final Runnable mJuggle = new Runnable() { + @Override + public void run() { + final int N = getChildCount(); + + final int K = 1; //irand(1,3); + for (int i=0; i<K; i++) { + final View child = getChildAt((int) (Math.random() * N)); + place(child, true); + } + + fillFreeList(); + + if (mStarted) { + mHandler.postDelayed(mJuggle, DELAY); + } + } + }; + + public DessertCaseView(Context context) { + this(context, null); + } + + public DessertCaseView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DessertCaseView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final Resources res = getResources(); + + mStarted = false; + + mCellSize = res.getDimensionPixelSize(R.dimen.dessert_case_cell_size); + final BitmapFactory.Options opts = new BitmapFactory.Options(); + if (mCellSize < 512) { // assuming 512x512 images + opts.inSampleSize = 2; + } + for (int[] list : new int[][] { PASTRIES, RARE_PASTRIES, XRARE_PASTRIES, XXRARE_PASTRIES }) { + for (int resid : list) { + final BitmapDrawable d = new BitmapDrawable(res, + BitmapFactory.decodeResource(res, resid, opts)); + d.setColorFilter(new ColorMatrixColorFilter(MASK)); + d.setBounds(0, 0, mCellSize, mCellSize); + mDrawables.append(resid, d); + } + } + if (DEBUG) setWillNotDraw(false); + } + + public void start() { + if (!mStarted) { + mStarted = true; + fillFreeList(DURATION * 4); + } + mHandler.postDelayed(mJuggle, START_DELAY); + } + + public void stop() { + mStarted = false; + mHandler.removeCallbacks(mJuggle); + } + + int pick(int[] a) { + return a[(int)(Math.random()*a.length)]; + } + + <T> T pick(T[] a) { + return a[(int)(Math.random()*a.length)]; + } + + <T> T pick(SparseArray<T> sa) { + return sa.valueAt((int)(Math.random()*sa.size())); + } + + float[] hsv = new float[] { 0, 1f, .85f }; + int random_color() { +// return 0xFF000000 | (int) (Math.random() * (float) 0xFFFFFF); // totally random + final int COLORS = 12; + hsv[0] = irand(0,COLORS) * (360f/COLORS); + return Color.HSVToColor(hsv); + } + + @Override + protected synchronized void onSizeChanged (int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (mWidth == w && mHeight == h) return; + + final boolean wasStarted = mStarted; + if (wasStarted) { + stop(); + } + + mWidth = w; + mHeight = h; + + mCells = null; + removeAllViewsInLayout(); + mFreeList.clear(); + + mRows = mHeight / mCellSize; + mColumns = mWidth / mCellSize; + + mCells = new View[mRows * mColumns]; + + if (DEBUG) Log.v(TAG, String.format("New dimensions: %dx%d", mColumns, mRows)); + + setScaleX(SCALE); + setScaleY(SCALE); + setTranslationX(0.5f * (mWidth - mCellSize * mColumns) * SCALE); + setTranslationY(0.5f * (mHeight - mCellSize * mRows) * SCALE); + + for (int j=0; j<mRows; j++) { + for (int i=0; i<mColumns; i++) { + mFreeList.add(new Point(i,j)); + } + } + + if (wasStarted) { + start(); + } + } + + public void fillFreeList() { + fillFreeList(DURATION); + } + + public synchronized void fillFreeList(int animationLen) { + final Context ctx = getContext(); + final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(mCellSize, mCellSize); + + while (! mFreeList.isEmpty()) { + Point pt = mFreeList.iterator().next(); + mFreeList.remove(pt); + final int i=pt.x; + final int j=pt.y; + + if (mCells[j*mColumns+i] != null) continue; + final ImageView v = new ImageView(ctx); + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + place(v, true); + postDelayed(new Runnable() { public void run() { fillFreeList(); } }, DURATION/2); + } + }); + + final int c = random_color(); + v.setBackgroundColor(c); + + final float which = frand(); + final Drawable d; + if (which < 0.001f) { + d = mDrawables.get(pick(XXRARE_PASTRIES)); + } else if (which < 0.01f) { + d = mDrawables.get(pick(XRARE_PASTRIES)); + } else if (which < 0.5f) { + d = mDrawables.get(pick(RARE_PASTRIES)); + } else if (which < 0.7f) { + d = mDrawables.get(pick(PASTRIES)); + } else { + d = null; + } + if (d != null) { + v.getOverlay().add(d); + } + + final Paint paint = new Paint(); + v.setLayerType(View.LAYER_TYPE_HARDWARE, paint); + + lp.width = lp.height = mCellSize; + addView(v, lp); + place(v, pt, false); + if (animationLen > 0) { + final float s = (Integer) v.getTag(TAG_SPAN); + v.setScaleX(0.5f * s); + v.setScaleY(0.5f * s); + v.setAlpha(0f); + v.animate().scaleX(s).scaleY(s).alpha(1f).setDuration(animationLen); + } + } + } + + public void place(View v, boolean animate) { + place(v, new Point(irand(0, mColumns), irand(0, mRows)), animate); + } + + private final HashSet<View> tmpSet = new HashSet<View>(); + public synchronized void place(View v, Point pt, boolean animate) { + final int i = pt.x; + final int j = pt.y; + final float rnd = frand(); + if (v.getTag(TAG_POS) != null) { + for (final Point oc : getOccupied(v)) { + mFreeList.add(oc); + mCells[oc.y*mColumns + oc.x] = null; + } + } + int scale = 1; + if (rnd < PROB_4X) { + if (!(i >= mColumns-3 || j >= mRows-3)) { + scale = 4; + } + } else if (rnd < PROB_3X) { + if (!(i >= mColumns-2 || j >= mRows-2)) { + scale = 3; + } + } else if (rnd < PROB_2X) { + if (!(i == mColumns-1 || j == mRows-1)) { + scale = 2; + } + } + + v.setTag(TAG_POS, pt); + v.setTag(TAG_SPAN, scale); + + tmpSet.clear(); + + final Point[] occupied = getOccupied(v); + for (final Point oc : occupied) { + final View squatter = mCells[oc.y*mColumns + oc.x]; + if (squatter != null) { + tmpSet.add(squatter); + } + } + + for (final View squatter : tmpSet) { + for (final Point sq : getOccupied(squatter)) { + mFreeList.add(sq); + mCells[sq.y*mColumns + sq.x] = null; + } + if (squatter != v) { + squatter.setTag(TAG_POS, null); + if (animate) { + squatter.animate().scaleX(0.5f).scaleY(0.5f).alpha(0) + .setDuration(DURATION) + .setInterpolator(new AccelerateInterpolator()) + .setListener(new Animator.AnimatorListener() { + public void onAnimationStart(Animator animator) { } + public void onAnimationEnd(Animator animator) { + removeView(squatter); + } + public void onAnimationCancel(Animator animator) { } + public void onAnimationRepeat(Animator animator) { } + }) + .start(); + } else { + removeView(squatter); + } + } + } + + for (final Point oc : occupied) { + mCells[oc.y*mColumns + oc.x] = v; + mFreeList.remove(oc); + } + + final float rot = (float)irand(0, 4) * 90f; + + if (animate) { + v.bringToFront(); + AnimatorSet set1 = new AnimatorSet(); + set1.playTogether( + ObjectAnimator.ofFloat(v, View.SCALE_X, (float) scale), + ObjectAnimator.ofFloat(v, View.SCALE_Y, (float) scale) + ); + set1.setInterpolator(new AnticipateOvershootInterpolator()); + set1.setDuration(DURATION); + set1.start(); + + AnimatorSet set2 = new AnimatorSet(); + set2.playTogether( + ObjectAnimator.ofFloat(v, View.ROTATION, rot), + ObjectAnimator.ofFloat(v, View.X, i* mCellSize + (scale-1) * mCellSize /2), + ObjectAnimator.ofFloat(v, View.Y, j* mCellSize + (scale-1) * mCellSize /2) + ); + set2.setInterpolator(new DecelerateInterpolator()); + set2.setDuration(DURATION); + set2.start(); + } else { + v.setX(i * mCellSize + (scale-1) * mCellSize /2); + v.setY(j * mCellSize + (scale-1) * mCellSize /2); + v.setScaleX((float) scale); + v.setScaleY((float) scale); + v.setRotation(rot); + } + } + + private Point[] getOccupied(View v) { + final int scale = (Integer) v.getTag(TAG_SPAN); + final Point pt = (Point)v.getTag(TAG_POS); + if (pt == null || scale == 0) return new Point[0]; + + final Point[] result = new Point[scale * scale]; + int p=0; + for (int i=0; i<scale; i++) { + for (int j=0; j<scale; j++) { + result[p++] = new Point(pt.x + i, pt.y + j); + } + } + return result; + } + + static float frand() { + return (float)(Math.random()); + } + + static float frand(float a, float b) { + return (frand() * (b-a) + a); + } + + static int irand(int a, int b) { + return (int)(frand(a, b)); + } + + @Override + public void onDraw(Canvas c) { + super.onDraw(c); + if (!DEBUG) return; + + Paint pt = new Paint(); + pt.setStyle(Paint.Style.STROKE); + pt.setColor(0xFFCCCCCC); + pt.setStrokeWidth(2.0f); + + final Rect check = new Rect(); + final int N = getChildCount(); + for (int i = 0; i < N; i++) { + View stone = getChildAt(i); + + stone.getHitRect(check); + + c.drawRect(check, pt); + } + } + + public static class RescalingContainer extends FrameLayout { + private static final int SYSTEM_UI_MODE_800 = 0x00000800; + private DessertCaseView mView; + private float mDarkness; + + public RescalingContainer(Context context) { + super(context); + + setSystemUiVisibility(0 + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | SYSTEM_UI_MODE_800 + ); + } + + public void setView(DessertCaseView v) { + addView(v); + mView = v; + } + + @Override + protected void onLayout (boolean changed, int left, int top, int right, int bottom) { + final float w = right-left; + final float h = bottom-top; + final int w2 = (int) (w / mView.SCALE / 2); + final int h2 = (int) (h / mView.SCALE / 2); + final int cx = (int) (left + w * 0.5f); + final int cy = (int) (top + h * 0.5f); + mView.layout(cx - w2, cy - h2, cx + w2, cy + h2); + } + + public void setDarkness(float p) { + mDarkness = p; + getDarkness(); + final int x = (int) (p * 0xff); + setBackgroundColor(x << 24 & 0xFF000000); + } + + public float getDarkness() { + return mDarkness; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java index c32f741b2ffa..c7f0e1746e80 100644 --- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java @@ -46,6 +46,7 @@ import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarPanel; +import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; import com.android.systemui.statusbar.phone.PhoneStatusBar; public class SearchPanelView extends FrameLayout implements @@ -88,11 +89,7 @@ public class SearchPanelView extends FrameLayout implements if (isKeyguardShowing) { // Have keyguard show the bouncer and launch the activity if the user succeeds. - try { - mWm.showAssistant(); - } catch (RemoteException e) { - // too bad, so sad... - } + KeyguardTouchDelegate.getInstance(getContext()).showAssistant(); onAnimationStarted(); } else { // Otherwise, keyguard isn't showing so launch it from here. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 49777d4ace82..932fe208c4fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -123,7 +123,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected int mCurrentUserId = 0; - protected int mLayoutDirection; + protected int mLayoutDirection = -1; // invalid private Locale mLocale; protected boolean mUseHeadsUp = false; @@ -299,8 +299,6 @@ public abstract class BaseStatusBar extends SystemUI implements IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mBroadcastReceiver, filter); - - mLocale = mContext.getResources().getConfiguration().locale; } public void userSwitched(int newUserId) { @@ -320,11 +318,17 @@ public abstract class BaseStatusBar extends SystemUI implements @Override protected void onConfigurationChanged(Configuration newConfig) { - final Locale newLocale = mContext.getResources().getConfiguration().locale; - if (! newLocale.equals(mLocale)) { - mLocale = newLocale; - mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); - refreshLayout(mLayoutDirection); + final Locale locale = mContext.getResources().getConfiguration().locale; + final int ld = TextUtils.getLayoutDirectionFromLocale(locale); + if (! locale.equals(mLocale) || ld != mLayoutDirection) { + if (DEBUG) { + Log.v(TAG, String.format( + "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, + locale, ld)); + } + mLocale = locale; + mLayoutDirection = ld; + refreshLayout(ld); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java index ecf7b3571de7..16fe1aa15922 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import android.content.res.Configuration; import android.provider.Settings; import android.util.Log; @@ -70,6 +71,13 @@ public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks { } @Override + protected void onConfigurationChanged(Configuration newConfig) { + if (mStatusBar != null) { + mStatusBar.onConfigurationChanged(newConfig); + } + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mStatusBar != null) { mStatusBar.dump(fd, pw, args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java index 1221a557a314..5c55f0da4a01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java @@ -23,7 +23,7 @@ import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; -import android.util.Log; +import android.util.Slog; import android.view.MotionEvent; import com.android.internal.policy.IKeyguardExitCallback; @@ -41,7 +41,9 @@ public class KeyguardTouchDelegate { static final String KEYGUARD_PACKAGE = "com.android.keyguard"; static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService"; - IKeyguardService mService; + private static KeyguardTouchDelegate sInstance; + + private volatile IKeyguardService mService; protected static final boolean DEBUG = false; protected static final String TAG = "KeyguardTouchDelegate"; @@ -49,83 +51,121 @@ public class KeyguardTouchDelegate { private final ServiceConnection mKeyguardConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - Log.v(TAG, "Connected to keyguard"); + Slog.v(TAG, "Connected to keyguard"); mService = IKeyguardService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { - Log.v(TAG, "Disconnected from keyguard"); + Slog.v(TAG, "Disconnected from keyguard"); mService = null; + sInstance = null; // force reconnection if this goes away } }; - public KeyguardTouchDelegate(Context context) { + private KeyguardTouchDelegate(Context context) { Intent intent = new Intent(); intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS); if (!context.bindServiceAsUser(intent, mKeyguardConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) { - if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS); + if (DEBUG) Slog.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS); } else { - if (DEBUG) Log.v(TAG, "*** Keyguard started"); + if (DEBUG) Slog.v(TAG, "*** Keyguard started"); + } + } + + public static KeyguardTouchDelegate getInstance(Context context) { + if (sInstance == null) { + sInstance = new KeyguardTouchDelegate(context); } + return sInstance; } public boolean isSecure() { - boolean secure = false; - if (mService != null) { + final IKeyguardService service = mService; + if (service != null) { try { - secure = mService.isSecure(); + return service.isSecure(); } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling keyguard.isSecure()!", e); + Slog.e(TAG, "RemoteException calling keyguard.isSecure()!", e); } } else { - Log.w(TAG, "isSecure(): NO SERVICE!"); + Slog.w(TAG, "isSecure(): NO SERVICE!"); } - return secure; + return false; } public boolean dispatch(MotionEvent event) { - if (mService != null) { + final IKeyguardService service = mService; + if (service != null) { try { - mService.dispatch(event); + service.dispatch(event); + return true; } catch (RemoteException e) { // What to do? - Log.e(TAG, "RemoteException sending event to keyguard!", e); - return false; + Slog.e(TAG, "RemoteException sending event to keyguard!", e); + } + } else { + Slog.w(TAG, "dispatch(event): NO SERVICE!"); + } + return false; + } + + public boolean isInputRestricted() { + final IKeyguardService service = mService; + if (service != null) { + try { + return service.isInputRestricted(); + } catch (RemoteException e) { + Slog.w(TAG , "Remote Exception", e); + } + } else { + Slog.w(TAG, "isInputRestricted(): NO SERVICE!"); + } + return false; + } + + public boolean isShowingAndNotHidden() { + final IKeyguardService service = mService; + if (service != null) { + try { + return service.isShowingAndNotHidden(); + } catch (RemoteException e) { + Slog.w(TAG , "Remote Exception", e); } - return true; } else { - Log.w(TAG, "dispatch(event): NO SERVICE!"); + Slog.w(TAG, "isShowingAndNotHidden(): NO SERVICE!"); } return false; } public void showAssistant() { - if (mService != null) { + final IKeyguardService service = mService; + if (service != null) { try { - mService.showAssistant(); + service.showAssistant(); } catch (RemoteException e) { // What to do? - Log.e(TAG, "RemoteException launching assistant!", e); + Slog.e(TAG, "RemoteException launching assistant!", e); } } else { - Log.w(TAG, "dispatch(event): NO SERVICE!"); + Slog.w(TAG, "showAssistant(event): NO SERVICE!"); } } public void launchCamera() { - if (mService != null) { + final IKeyguardService service = mService; + if (service != null) { try { - mService.launchCamera(); + service.launchCamera(); } catch (RemoteException e) { // What to do? - Log.e(TAG, "RemoteException launching camera!", e); + Slog.e(TAG, "RemoteException launching camera!", e); } } else { - Log.w(TAG, "dispatch(event): NO SERVICE!"); + Slog.w(TAG, "dispatch(event): NO SERVICE!"); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 596fac601398..04885f0b076f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -88,7 +88,6 @@ public class NavigationBarView extends LinearLayout { // used to disable the camera icon in navbar when disabled by DPM private boolean mCameraDisabledByDpm; - KeyguardTouchDelegate mKeyguardTouchDelegate; private final OnTouchListener mCameraTouchListener = new OnTouchListener() { @Override @@ -112,7 +111,7 @@ public class NavigationBarView extends LinearLayout { } break; } - return mKeyguardTouchDelegate.dispatch(event); + return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event); } }; @@ -155,8 +154,6 @@ public class NavigationBarView extends LinearLayout { mBarTransitions = new NavigationBarTransitions(this); - mKeyguardTouchDelegate = new KeyguardTouchDelegate(mContext); - mCameraDisabledByDpm = isCameraDisabledByDpm(); watchForDevicePolicyChanges(); } @@ -341,7 +338,7 @@ public class NavigationBarView extends LinearLayout { final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId); final boolean disabledBecauseKeyguardSecure = (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 - && mKeyguardTouchDelegate.isSecure(); + && KeyguardTouchDelegate.getInstance(getContext()).isSecure(); return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure; } catch (RemoteException e) { Log.e(TAG, "Can't get userId", e); @@ -426,9 +423,9 @@ public class NavigationBarView extends LinearLayout { protected void launchForAccessibilityClick(View v) { if (v == getCameraButton()) { - mKeyguardTouchDelegate.launchCamera(); + KeyguardTouchDelegate.getInstance(getContext()).launchCamera(); } else if (v == getSearchLight()) { - mKeyguardTouchDelegate.showAssistant(); + KeyguardTouchDelegate.getInstance(getContext()).showAssistant(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index fbc94b1ef613..c47d0eb7d60c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -38,6 +38,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Canvas; @@ -633,7 +634,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // receive broadcasts IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); @@ -2433,17 +2433,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { notifyNavigationBarScreenOn(false); notifyHeadsUpScreenOn(false); } - else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { - if (DEBUG) { - Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); - } - mDisplay.getSize(mCurrentDisplaySize); - - updateResources(); - repositionNavigationBar(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - updateShowSearchHoldoff(); - } else if (Intent.ACTION_SCREEN_ON.equals(action)) { mScreenOn = true; // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) @@ -2466,6 +2455,22 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } }; + // SystemUIService notifies SystemBars of configuration changes, which then calls down here + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); // calls refreshLayout + + if (DEBUG) { + Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); + } + mDisplay.getSize(mCurrentDisplaySize); + + updateResources(); + repositionNavigationBar(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + updateShowSearchHoldoff(); + } + @Override public void userSwitched(int newUserId) { if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 9f9b6d6b332b..a5fd1d7b8eb5 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -5127,11 +5127,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void showAssistant() { - mKeyguardDelegate.showAssistant(); - } - - @Override public boolean canMagnifyWindow(int windowType) { switch (windowType) { case WindowManager.LayoutParams.TYPE_INPUT_METHOD: diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java index 5e299ee9a25b..83be1a801b88 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java @@ -181,11 +181,7 @@ public class KeyguardServiceWrapper implements IKeyguardService { } public void showAssistant() { - try { - mService.showAssistant(); - } catch (RemoteException e) { - Slog.w(TAG , "Remote Exception", e); - } + // Not used by PhoneWindowManager } public void dispatch(MotionEvent event) { diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index 2145b76b39e5..3a3dfd5f1c04 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -47,6 +47,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; +import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.Cursor; import android.database.DatabaseUtils; @@ -580,15 +581,18 @@ public class AccountManagerService try { new Session(fromAccounts, null, account.type, false, false /* stripAuthTokenFromResult */) { + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", getAccountCredentialsForClone" + ", " + account.type; } + @Override public void run() throws RemoteException { mAuthenticator.getAccountCredentialsForCloning(this, account); } + @Override public void onResult(Bundle result) { if (result != null) { if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { @@ -613,11 +617,13 @@ public class AccountManagerService try { new Session(targetUser, null, account.type, false, false /* stripAuthTokenFromResult */) { + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", getAccountCredentialsForClone" + ", " + account.type; } + @Override public void run() throws RemoteException { // Confirm that the owner's account still exists before this step. UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); @@ -632,6 +638,7 @@ public class AccountManagerService } } + @Override public void onResult(Bundle result) { if (result != null) { if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { @@ -646,6 +653,7 @@ public class AccountManagerService } } + @Override public void onError(int errorCode, String errorMessage) { super.onError(errorCode, errorMessage); // TODO: Show error notification to user @@ -774,6 +782,7 @@ public class AccountManagerService mAccount = account; } + @Override public void run() throws RemoteException { try { mAuthenticator.hasFeatures(this, mAccount, mFeatures); @@ -782,6 +791,7 @@ public class AccountManagerService } } + @Override public void onResult(Bundle result) { IAccountManagerResponse response = getResponseAndClose(); if (response != null) { @@ -807,6 +817,7 @@ public class AccountManagerService } } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", hasFeatures" + ", " + mAccount @@ -863,15 +874,18 @@ public class AccountManagerService mAccount = account; } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", removeAccount" + ", account " + mAccount; } + @Override public void run() throws RemoteException { mAuthenticator.getAccountRemovalAllowed(this, mAccount); } + @Override public void onResult(Bundle result) { if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) && !result.containsKey(AccountManager.KEY_INTENT)) { @@ -1212,16 +1226,19 @@ public class AccountManagerService try { new Session(accounts, response, accountType, false, false /* stripAuthTokenFromResult */) { + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", getAuthTokenLabel" + ", " + accountType + ", authTokenType " + authTokenType; } + @Override public void run() throws RemoteException { mAuthenticator.getAuthTokenLabel(this, authTokenType); } + @Override public void onResult(Bundle result) { if (result != null) { String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL); @@ -1299,6 +1316,7 @@ public class AccountManagerService new Session(accounts, response, account.type, expectActivityLaunch, false /* stripAuthTokenFromResult */) { + @Override protected String toDebugString(long now) { if (loginOptions != null) loginOptions.keySet(); return super.toDebugString(now) + ", getAuthToken" @@ -1308,6 +1326,7 @@ public class AccountManagerService + ", notifyOnAuthFailure " + notifyOnAuthFailure; } + @Override public void run() throws RemoteException { // If the caller doesn't have permission then create and return the // "grant permission" intent instead of the "getAuthToken" intent. @@ -1318,6 +1337,7 @@ public class AccountManagerService } } + @Override public void onResult(Bundle result) { if (result != null) { if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) { @@ -1482,11 +1502,13 @@ public class AccountManagerService try { new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */) { + @Override public void run() throws RemoteException { mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures, options); } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", addAccount" + ", accountType " + accountType @@ -1530,9 +1552,11 @@ public class AccountManagerService try { new Session(accounts, response, account.type, expectActivityLaunch, true /* stripAuthTokenFromResult */) { + @Override public void run() throws RemoteException { mAuthenticator.confirmCredentials(this, account, options); } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", confirmCredentials" + ", " + account; @@ -1563,9 +1587,11 @@ public class AccountManagerService try { new Session(accounts, response, account.type, expectActivityLaunch, true /* stripAuthTokenFromResult */) { + @Override public void run() throws RemoteException { mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); } + @Override protected String toDebugString(long now) { if (loginOptions != null) loginOptions.keySet(); return super.toDebugString(now) + ", updateCredentials" @@ -1596,9 +1622,11 @@ public class AccountManagerService try { new Session(accounts, response, accountType, expectActivityLaunch, true /* stripAuthTokenFromResult */) { + @Override public void run() throws RemoteException { mAuthenticator.editProperties(this, mAccountType); } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", editProperties" + ", accountType " + accountType; @@ -1624,6 +1652,7 @@ public class AccountManagerService mFeatures = features; } + @Override public void run() throws RemoteException { synchronized (mAccounts.cacheLock) { mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, @@ -1661,6 +1690,7 @@ public class AccountManagerService } } + @Override public void onResult(Bundle result) { mNumResults++; if (result == null) { @@ -1699,6 +1729,7 @@ public class AccountManagerService } + @Override protected String toDebugString(long now) { return super.toDebugString(now) + ", getAccountsByTypeAndFeatures" + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); @@ -2111,9 +2142,31 @@ public class AccountManagerService } } + @Override public void onResult(Bundle result) { mNumResults++; - if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { + Intent intent = null; + if (result != null + && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { + /* + * The Authenticator API allows third party authenticators to + * supply arbitrary intents to other apps that they can run, + * this can be very bad when those apps are in the system like + * the System Settings. + */ + PackageManager pm = mContext.getPackageManager(); + ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + int targetUid = resolveInfo.activityInfo.applicationInfo.uid; + int authenticatorUid = Binder.getCallingUid(); + if (PackageManager.SIGNATURE_MATCH != + pm.checkSignatures(authenticatorUid, targetUid)) { + throw new SecurityException( + "Activity to be started with KEY_INTENT must " + + "share Authenticator's signatures"); + } + } + if (result != null + && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { @@ -2223,6 +2276,7 @@ public class AccountManagerService super(looper); } + @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_TIMED_OUT: diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index a14d729a805d..ddb6d1abd2d2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1689,7 +1689,7 @@ public final class ActivityManagerService extends ActivityManagerNative && proc.pid == pid) { num++; proc.lastPssTime = SystemClock.uptimeMillis(); - proc.baseProcessTracker.addPss(pss, tmp[0], true); + proc.baseProcessTracker.addPss(pss, tmp[0], true, proc.pkgList); if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss + " state=" + ProcessList.makeProcStateString(procState)); @@ -2705,18 +2705,6 @@ public final class ActivityManagerService extends ActivityManagerNative return intent; } - String getHomePackageName() { - Intent intent = getHomeIntent(); - ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, mCurrentUserId); - if (aInfo != null) { - final String homePackageName = aInfo.applicationInfo.packageName; - if (!ResolverActivity.class.getName().equals(homePackageName)) { - return homePackageName; - } - } - return null; - } - boolean startHomeActivityLocked(int userId) { if (mHeadless) { // Added because none of the other calls to ensureBootCompleted seem to fire @@ -4278,7 +4266,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. proc.baseProcessTracker.addPss(infos[i].getTotalPss(), - infos[i].getTotalUss(), false); + infos[i].getTotalUss(), false, proc.pkgList); } } } @@ -4305,7 +4293,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false); + proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, proc.pkgList); } } } @@ -6786,6 +6774,10 @@ public final class ActivityManagerService extends ActivityManagerNative // Kill the running processes. for (int i=0; i<procs.size(); i++) { ProcessRecord pr = procs.get(i); + if (pr == mHomeProcess) { + // Don't kill the home process along with tasks from the same package. + continue; + } if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { killUnneededProcessLocked(pr, "remove task"); } else { @@ -11748,7 +11740,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true); + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList); } } @@ -11909,7 +11901,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (memInfo.getZramTotalSizeKb() != 0) { if (!isCompact) { pw.print(" ZRAM: "); pw.print(memInfo.getZramTotalSizeKb()); - pw.print(" kB used for "); + pw.print(" kB physical used for "); pw.print(memInfo.getSwapTotalSizeKb() - memInfo.getSwapFreeSizeKb()); pw.print(" kB in swap ("); diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 43598954d32c..2c0b83bef0b8 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -58,6 +58,7 @@ import java.util.HashSet; final class ActivityRecord { static final String TAG = ActivityManagerService.TAG; static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE; + final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recent"; final ActivityManagerService service; // owner final IApplicationToken.Stub appToken; // window manager token @@ -443,25 +444,18 @@ final class ActivityRecord { noDisplay = ent != null && ent.array.getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); - // If we know the system has determined the component, then - // we can consider this to be a home activity... - String homePackageName = supervisor.getHomePackageName(); - if (homePackageName != null && homePackageName.equals(packageName)) { - mActivityType = HOME_ACTIVITY_TYPE; - } else if ((!_componentSpecified || _launchedFromUid == Process.myUid() + if ((!_componentSpecified || _launchedFromUid == Process.myUid() || _launchedFromUid == 0) && Intent.ACTION_MAIN.equals(_intent.getAction()) && _intent.hasCategory(Intent.CATEGORY_HOME) && _intent.getCategories().size() == 1 && _intent.getData() == null && _intent.getType() == null && - (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && + isNotResolverActivity()) { // This sure looks like a home activity! mActivityType = HOME_ACTIVITY_TYPE; - if (isNotResolverActivity()) { - supervisor.setHomePackageName(userId, packageName); - } - } else if (realActivity.getClassName().contains("com.android.systemui.recent")) { + } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) { mActivityType = RECENTS_ACTIVITY_TYPE; } else { mActivityType = APPLICATION_ACTIVITY_TYPE; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index a88c3cc08614..4d669461d35d 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -334,20 +334,16 @@ final class ActivityStack { mCurrentUser = service.mCurrentUserId; } - private boolean okToShow(ActivityRecord r) { + boolean okToShow(ActivityRecord r) { return r.userId == mCurrentUser || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0; } final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { - final TaskRecord task = mTaskHistory.get(taskNdx); - final ArrayList<ActivityRecord> activities = task.mActivities; - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - ActivityRecord r = activities.get(activityNdx); - if (!r.finishing && r != notTop && okToShow(r)) { - return r; - } + ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(notTop); + if (r != null) { + return r; } } return null; @@ -937,6 +933,10 @@ final class ActivityStack { next.idle = false; next.results = null; next.newIntents = null; + if (next.nowVisible) { + // We won't get a call to reportActivityVisibleLocked() so dismiss lockscreen now. + mStackSupervisor.dismissKeyguard(); + } // schedule an idle timeout in case the app doesn't do it for us. mStackSupervisor.scheduleIdleTimeoutLocked(next); @@ -1083,10 +1083,24 @@ final class ActivityStack { // At this point, nothing else needs to be shown if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r); behindFullscreen = true; - } else if (task.mActivities.indexOf(r) == 0 && task.mOnTopOfHome) { - if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r); - showHomeBehindStack = true; - behindFullscreen = true; + } else if (task.mOnTopOfHome) { + // Work our way down from r to bottom of task and see if there are any + // visible activities below r. + int rIndex = task.mActivities.indexOf(r); + for ( --rIndex; rIndex >= 0; --rIndex) { + final ActivityRecord blocker = task.mActivities.get(rIndex); + if (!blocker.finishing && blocker.visible) { + if (DEBUG_VISBILITY) Slog.v(TAG, "Home visibility for " + + r + " blocked by " + blocker); + break; + } + } + if (rIndex < 0) { + // Got to task bottom without finding a visible activity, show home. + if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r); + showHomeBehindStack = true; + behindFullscreen = true; + } } } else { if (DEBUG_VISBILITY) Slog.v( @@ -3401,11 +3415,16 @@ final class ActivityStack { } } - void handleAppDiedLocked(ProcessRecord app, boolean restarting) { + /** + * Reset local parameters because an app's activity died. + * @param app The app of the activity that died. + * @return true if home should be launched next. + */ + boolean handleAppDiedLocked(ProcessRecord app) { if (!containsApp(app)) { - return; + return false; } - // TODO: handle the case where an app spans multiple stacks. + if (mPausingActivity != null && mPausingActivity.app == app) { if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG, "App died while pausing: " + mPausingActivity); @@ -3415,28 +3434,32 @@ final class ActivityStack { mLastPausedActivity = null; mLastNoHistoryActivity = null; } - final ActivityRecord top = topRunningActivityLocked(null); - final boolean launchHomeTaskNext = - top != null && top.app == app && top.task.mOnTopOfHome; - - // Remove this application's activities from active lists. - boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app); - if (!restarting) { - ActivityStack stack = mStackSupervisor.getFocusedStack(); - if (stack == null || launchHomeTaskNext) { - mStackSupervisor.resumeHomeActivity(null); - } else if (!mStackSupervisor.resumeTopActivitiesLocked(stack, null, null)) { - // If there was nothing to resume, and we are not already - // restarting this process, but there is a visible activity that - // is hosted by the process... then make sure all visible - // activities are running, taking care of restarting this - // process. - if (hasVisibleActivities) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); - } + // Determine if the top task is exiting and should return to home. Do this before it gets + // removed in removeHistoryRecordsForAppsLocked. + boolean launchHomeNext = false; + int top = mTaskHistory.size() - 1; + while (top >= 0) { + final TaskRecord topTask = mTaskHistory.get(top); + if (topTask.mActivities.isEmpty()) { + // Not possible, but just in case. + --top; + continue; + } + ActivityRecord r = topTask.topRunningActivityLocked(null); + if (r != null) { + // r will be launched next. + break; } + // There is an activity in topTask that is finishing. If topTask belongs to the app + // return to home depending on the task flag. + launchHomeNext = topTask.mOnTopOfHome; + break; } + + removeHistoryRecordsForAppLocked(app); + + return launchHomeNext; } void handleAppCrashLocked(ProcessRecord app) { diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 1ee13ec96ed1..f71870633fdc 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -203,12 +203,6 @@ public final class ActivityStackSupervisor { */ final PowerManager.WakeLock mGoingToSleep; - /** - * The name of the current home activity for each user. - * TODO: Remove entries when user is deleted. - */ - final SparseArray<String> mHomePackageNames = new SparseArray<String>(); - public ActivityStackSupervisor(ActivityManagerService service, Context context, Looper looper) { mService = service; @@ -1932,10 +1926,28 @@ public final class ActivityStackSupervisor { } void handleAppDiedLocked(ProcessRecord app, boolean restarting) { - // Just in case. + boolean launchHomeTaskNext = false; + final ActivityStack focusedStack = getFocusedStack(); final int numStacks = mStacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - mStacks.get(stackNdx).handleAppDiedLocked(app, restarting); + final ActivityStack stack = mStacks.get(stackNdx); + // Only update launchHomeTaskNext for the focused stack. + launchHomeTaskNext |= (stack == focusedStack && stack.handleAppDiedLocked(app)); + } + + if (!restarting) { + if (launchHomeTaskNext) { + resumeHomeActivity(null); + } else { + if (!resumeTopActivitiesLocked(focusedStack, null, null)) { + // If there was nothing to resume, and we are not already + // restarting this process, but there is a visible activity that + // is hosted by the process... then make sure all visible + // activities are running, taking care of restarting this + // process. + ensureActivitiesVisibleLocked(null, 0); + } + } } } @@ -2267,11 +2279,6 @@ public final class ActivityStackSupervisor { boolean switchUserLocked(int userId, UserStartedState uss) { mCurrentUser = userId; - final String homePackageName = mService.getHomePackageName(); - if (homePackageName != null) { - setHomePackageName(mCurrentUser, homePackageName); - } - mStartingUsers.add(uss); boolean haveActivities = false; for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { @@ -2373,12 +2380,6 @@ public final class ActivityStackSupervisor { pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState)); pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout); pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId); - pw.print(prefix); pw.print("mHomePackageNames:"); - for (int i = 0; i < mHomePackageNames.size(); ++i) { - pw.print(" ("); pw.print(mHomePackageNames.keyAt(i)); pw.print(","); - pw.print(mHomePackageNames.valueAt(i)); pw.print(")"); - } - pw.println(); } ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) { @@ -2635,14 +2636,4 @@ public final class ActivityStackSupervisor { } } } - - String getHomePackageName() { - return mHomePackageNames.get(mCurrentUser); - } - - void setHomePackageName(int userId, String homePackageName) { - if (DEBUG_SWITCH) Slog.d(TAG, "setHomePackageName: user=" + userId + " package=" - + homePackageName); - mHomePackageNames.put(userId, homePackageName); - } } diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java index be089731bb34..2c49bb991d9e 100644 --- a/services/java/com/android/server/am/ProcessStatsService.java +++ b/services/java/com/android/server/am/ProcessStatsService.java @@ -61,7 +61,6 @@ public final class ProcessStatsService extends IProcessStats.Stub { static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. - static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours. final ActivityManagerService mAm; final File mBaseDir; @@ -160,7 +159,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { public boolean shouldWriteNowLocked(long now) { if (now > (mLastWriteTime+WRITE_PERIOD)) { if (SystemClock.elapsedRealtime() - > (mProcessStats.mTimePeriodStartRealtime+COMMIT_PERIOD)) { + > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) { mCommitPending = true; } return true; @@ -358,7 +357,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage) { ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked( - screenStates, memStates, procStates, now, reqPackage); + screenStates, memStates, procStates, procStates, now, reqPackage); if (procs.size() > 0) { if (header != null) { pw.println(header); @@ -457,7 +456,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { if (curTime < minTime) { // Need to add in older stats to reach desired time. ArrayList<String> files = getCommittedFiles(0, false, true); - if (files.size() > 0) { + if (files != null && files.size() > 0) { current.setDataPosition(0); ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); current.recycle(); @@ -520,7 +519,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { static private void dumpHelp(PrintWriter pw) { pw.println("Process stats (procstats) dump options:"); pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); - pw.println(" [--details] [--full-details] [--current] [--one-day]"); + pw.println(" [--details] [--full-details] [--current] [--hours]"); pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]"); pw.println(" --checkin: perform a checkin: print and delete old committed states."); pw.println(" --c: print only state in checkin format."); @@ -532,7 +531,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { pw.println(" --details: dump all execution details, not just summary."); pw.println(" --full-details: dump only detail information, for all saved state."); pw.println(" --current: only dump current state."); - pw.println(" --one-day: dump stats aggregated across about one day."); + pw.println(" --hours: aggregate over about N last hours."); pw.println(" --commit: commit current stats to disk and reset to start new stats."); pw.println(" --reset: reset current stats, without committing."); pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); @@ -562,7 +561,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean dumpDetails = false; boolean dumpFullDetails = false; boolean dumpAll = false; - boolean oneDay = false; + int aggregateHours = 0; String reqPackage = null; boolean csvSepScreenStats = false; int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON}; @@ -632,8 +631,20 @@ public final class ProcessStatsService extends IProcessStats.Stub { dumpDetails = true; } else if ("--full-details".equals(arg)) { dumpFullDetails = true; - } else if ("--one-day".equals(arg)) { - oneDay = true; + } else if ("--hours".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --hours"); + dumpHelp(pw); + return; + } + try { + aggregateHours = Integer.parseInt(args[i]); + } catch (NumberFormatException e) { + pw.println("Error: --hours argument not an int -- " + args[i]); + dumpHelp(pw); + return; + } } else if ("--current".equals(arg)) { currentOnly = true; } else if ("--commit".equals(arg)) { @@ -750,8 +761,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { */ } return; - } else if (oneDay) { - ParcelFileDescriptor pfd = getStatsOverTime(24*60*60*1000); + } else if (aggregateHours != 0) { + ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 + - (ProcessStats.COMMIT_PERIOD/2)); if (pfd == null) { pw.println("Unable to build stats!"); return; diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index f0bba4f99726..385253ef5bce 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -139,6 +139,16 @@ final class TaskRecord extends ThumbnailHolder { return null; } + ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { + for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { + ActivityRecord r = mActivities.get(activityNdx); + if (!r.finishing && r != notTop && stack.okToShow(r)) { + return r; + } + } + return null; + } + /** * Reorder the history stack so that the activity at the given index is * brought to the front. @@ -418,6 +428,7 @@ final class TaskRecord extends ThumbnailHolder { pw.print(prefix); pw.print("numActivities="); pw.print(numActivities); pw.print(" rootWasReset="); pw.print(rootWasReset); pw.print(" userId="); pw.print(userId); + pw.print(" mTaskType="); pw.print(mTaskType); pw.print(" numFullscreen="); pw.print(numFullscreen); pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome); } diff --git a/services/java/com/android/server/connectivity/PacManager.java b/services/java/com/android/server/connectivity/PacManager.java index 53e1dc218c3d..1cb2fe384132 100644 --- a/services/java/com/android/server/connectivity/PacManager.java +++ b/services/java/com/android/server/connectivity/PacManager.java @@ -86,6 +86,9 @@ public class PacManager { private int mCurrentDelay; private int mLastPort; + private boolean mHasSentBroadcast; + private boolean mHasDownloaded; + /** * Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac. */ @@ -110,6 +113,8 @@ public class PacManager { setCurrentProxyScript(file); } } + mHasDownloaded = true; + sendProxyIfNeeded(); longSchedule(); } else { reschedule(); @@ -155,6 +160,8 @@ public class PacManager { mPacUrl = proxy.getPacFileUrl(); } mCurrentDelay = DELAY_1; + mHasSentBroadcast = false; + mHasDownloaded = false; getAlarmManager().cancel(mPacRefreshIntent); bind(); return true; @@ -311,10 +318,14 @@ public class PacManager { callbackService.getProxyPort(new IProxyPortListener.Stub() { @Override public void setProxyPort(int port) throws RemoteException { + if (mLastPort != -1) { + // Always need to send if port changed + mHasSentBroadcast = false; + } mLastPort = port; if (port != -1) { Log.d(TAG, "Local proxy is bound on " + port); - sendPacBroadcast(new ProxyProperties(mPacUrl, port)); + sendProxyIfNeeded(); } else { Log.e(TAG, "Received invalid port from Local Proxy," + " PAC will not be operational"); @@ -341,6 +352,7 @@ public class PacManager { mProxyConnection = null; } mProxyService = null; + mLastPort = -1; } private void sendPacBroadcast(ProxyProperties proxy) { @@ -355,4 +367,14 @@ public class PacManager { Binder.restoreCallingIdentity(ident); } } + + private synchronized void sendProxyIfNeeded() { + if (!mHasDownloaded || (mLastPort == -1)) { + return; + } + if (!mHasSentBroadcast) { + sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort)); + mHasSentBroadcast = true; + } + } } diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index d4583b561898..b8e1b0404f3c 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -194,6 +194,21 @@ public final class PrintManagerService extends IPrintManager.Stub { } @Override + public List<PrintServiceInfo> getInstalledPrintServices(int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + return userState.getInstalledPrintServices(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer, int userId) { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index f98a8053fcd2..798cea36caaf 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -33,7 +33,6 @@ import android.print.IPrintSpoolerCallbacks; import android.print.IPrintSpoolerClient; import android.print.PrintJobId; import android.print.PrintJobInfo; -import android.print.PrintManager; import android.util.Slog; import android.util.TimedRemoteCaller; @@ -91,7 +90,7 @@ final class RemotePrintSpooler { public static interface PrintSpoolerCallbacks { public void onPrintJobQueued(PrintJobInfo printJob); public void onAllPrintJobsForServiceHandled(ComponentName printService); - public void onPrintJobStateChanged(PrintJobId printJobId, int appId); + public void onPrintJobStateChanged(PrintJobInfo printJob); } public RemotePrintSpooler(Context context, int userId, @@ -280,30 +279,6 @@ final class RemotePrintSpooler { } } - public final void forgetPrintJobs(List<PrintJobId> printJobIds) { - throwIfCalledOnMainThread(); - synchronized (mLock) { - throwIfDestroyedLocked(); - mCanUnbind = false; - } - try { - getRemoteInstanceLazy().forgetPrintJobs(printJobIds); - } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error forgeting print jobs", re); - } catch (TimeoutException te) { - Slog.e(LOG_TAG, "Error forgeting print jobs", te); - } finally { - if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() - + "] forgetPrintJobs()"); - } - synchronized (mLock) { - mCanUnbind = true; - mLock.notifyAll(); - } - } - } - public final void destroy() { throwIfCalledOnMainThread(); if (DEBUG) { @@ -323,18 +298,15 @@ final class RemotePrintSpooler { .append(String.valueOf(mDestroyed)).println(); pw.append(prefix).append("bound=") .append((mRemoteInstance != null) ? "true" : "false").println(); - pw.append(prefix).append("print jobs:").println(); - if (mRemoteInstance != null) { - List<PrintJobInfo> printJobs = getPrintJobInfos(null, - PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY); - if (printJobs != null) { - final int printJobCount = printJobs.size(); - for (int i = 0; i < printJobCount; i++) { - PrintJobInfo printJob = printJobs.get(i); - pw.append(prefix).append(prefix).append(printJob.toString()); - pw.println(); - } - } + + pw.flush(); + + try { + getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix}); + } catch (TimeoutException te) { + /* ignore */ + } catch (RemoteException re) { + /* ignore */ } } } @@ -346,8 +318,8 @@ final class RemotePrintSpooler { } } - private void onPrintJobStateChanged(PrintJobId printJobId, int appId) { - mCallbacks.onPrintJobStateChanged(printJobId, appId); + private void onPrintJobStateChanged(PrintJobInfo printJob) { + mCallbacks.onPrintJobStateChanged(printJob); } private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { @@ -625,12 +597,12 @@ final class RemotePrintSpooler { } @Override - public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { + public void onPrintJobStateChanged(PrintJobInfo printJob) { RemotePrintSpooler spooler = mWeakSpooler.get(); if (spooler != null) { final long identity = Binder.clearCallingIdentity(); try { - spooler.onPrintJobStateChanged(printJobId, appId); + spooler.onPrintJobStateChanged(printJob); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index e5f5842d7e26..bc70fe30c554 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -33,7 +33,6 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.UserManager; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; import android.print.IPrintJobStateChangeListener; @@ -52,6 +51,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.R; import com.android.internal.os.BackgroundThread; @@ -62,6 +62,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -93,8 +94,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { private final Set<ComponentName> mEnabledServices = new ArraySet<ComponentName>(); - private final CreatedPrintJobTracker mCreatedPrintJobTracker = - new CreatedPrintJobTracker(); + private final PrintJobForAppCache mPrintJobForAppCache = + new PrintJobForAppCache(); private final Object mLock; @@ -155,23 +156,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { public PrintJobInfo print(String printJobName, final IPrintClient client, final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) { - PrintJobId printJobId = new PrintJobId(); - - // Track this job so we can forget it when the creator dies. - if (!mCreatedPrintJobTracker.onPrintJobCreatedLocked(client.asBinder(), printJobId)) { - // Not adding a print job means the client is dead - done. - return null; - } - // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); - printJob.setId(printJobId); + printJob.setId(new PrintJobId()); printJob.setAppId(appId); printJob.setLabel(printJobName); printJob.setAttributes(attributes); printJob.setState(PrintJobInfo.STATE_CREATED); printJob.setCopies(1); + // Track this job so we can forget it when the creator dies. + if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId, + printJob)) { + // Not adding a print job means the client is dead - done. + return null; + } + // Spin the spooler to add the job and show the config UI. new AsyncTask<Void, Void, Void>() { @Override @@ -185,10 +185,42 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } public List<PrintJobInfo> getPrintJobInfos(int appId) { - return mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId); + List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId); + // Note that the print spooler is not storing print jobs that + // are in a terminal state as it is non-trivial to properly update + // the spooler state for when to forget print jobs in terminal state. + // Therefore, we fuse the cached print jobs for running apps (some + // jobs are in a terminal state) with the ones that the print + // spooler knows about (some jobs are being processed). + ArrayMap<PrintJobId, PrintJobInfo> result = + new ArrayMap<PrintJobId, PrintJobInfo>(); + + // Add the cached print jobs for running apps. + final int cachedPrintJobCount = cachedPrintJobs.size(); + for (int i = 0; i < cachedPrintJobCount; i++) { + PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i); + result.put(cachedPrintJob.getId(), cachedPrintJob); + } + + // Add everything else the spooler knows about. + List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null, + PrintJobInfo.STATE_ANY, appId); + if (printJobs != null) { + final int printJobCount = printJobs.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo printJob = printJobs.get(i); + result.put(printJob.getId(), printJob); + } + } + + return new ArrayList<PrintJobInfo>(result.values()); } public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { + PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); + if (printJob != null) { + return printJob; + } return mSpooler.getPrintJobInfo(printJobId, appId); } @@ -242,6 +274,12 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } + public List<PrintServiceInfo> getInstalledPrintServices() { + synchronized (mLock) { + return mInstalledServices; + } + } + public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); @@ -398,9 +436,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } @Override - public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { + public void onPrintJobStateChanged(PrintJobInfo printJob) { + mPrintJobForAppCache.onPrintJobStateChanged(printJob); mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, - appId, 0, printJobId).sendToTarget(); + printJob.getAppId(), 0, printJob.getId()).sendToTarget(); } @Override @@ -525,6 +564,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { pw.println(); } + pw.append(prefix).append(tab).append("cached print jobs:").println(); + mPrintJobForAppCache.dump(pw, prefix + tab + tab); + pw.append(prefix).append(tab).append("discovery mediator:").println(); if (mPrinterDiscoverySession != null) { mPrinterDiscoverySession.dump(pw, prefix + tab + tab); @@ -1424,34 +1466,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } - private final class CreatedPrintJobTracker { - private final ArrayMap<IBinder, List<PrintJobId>> mCreatedPrintJobs = - new ArrayMap<IBinder, List<PrintJobId>>(); + private final class PrintJobForAppCache { + private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = + new SparseArray<List<PrintJobInfo>>(); - public boolean onPrintJobCreatedLocked(final IBinder creator, PrintJobId printJobId) { + public boolean onPrintJobCreated(final IBinder creator, final int appId, + PrintJobInfo printJob) { try { creator.linkToDeath(new DeathRecipient() { @Override public void binderDied() { creator.unlinkToDeath(this, 0); - UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); - // If the death is a result of the user being removed, then - // do nothing since the spooler data for this user will be - // wiped and we cannot bind to the spooler at this point. - if (userManager.getUserInfo(mUserId) == null) { - return; - } - List<PrintJobId> printJobIds = null; synchronized (mLock) { - printJobIds = mCreatedPrintJobs.remove(creator); - if (printJobIds == null) { - return; - } - printJobIds = new ArrayList<PrintJobId>(printJobIds); - } - if (printJobIds != null) { - mSpooler.forgetPrintJobs(printJobIds); + mPrintJobsForRunningApp.remove(appId); } } }, 0); @@ -1460,14 +1487,93 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return false; } synchronized (mLock) { - List<PrintJobId> printJobIds = mCreatedPrintJobs.get(creator); - if (printJobIds == null) { - printJobIds = new ArrayList<PrintJobId>(); - mCreatedPrintJobs.put(creator, printJobIds); + List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); + if (printJobsForApp == null) { + printJobsForApp = new ArrayList<PrintJobInfo>(); + mPrintJobsForRunningApp.put(appId, printJobsForApp); } - printJobIds.add(printJobId); + printJobsForApp.add(printJob); } return true; } + + public void onPrintJobStateChanged(PrintJobInfo printJob) { + synchronized (mLock) { + List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( + printJob.getAppId()); + if (printJobsForApp == null) { + return; + } + final int printJobCount = printJobsForApp.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo oldPrintJob = printJobsForApp.get(i); + if (oldPrintJob.getId().equals(printJob.getId())) { + printJobsForApp.set(i, printJob); + } + } + } + } + + public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { + synchronized (mLock) { + List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); + if (printJobsForApp == null) { + return null; + } + final int printJobCount = printJobsForApp.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo printJob = printJobsForApp.get(i); + if (printJob.getId().equals(printJobId)) { + return printJob; + } + } + } + return null; + } + + public List<PrintJobInfo> getPrintJobs(int appId) { + synchronized (mLock) { + List<PrintJobInfo> printJobs = null; + if (appId == PrintManager.APP_ID_ANY) { + final int bucketCount = mPrintJobsForRunningApp.size(); + for (int i = 0; i < bucketCount; i++) { + List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); + if (printJobs == null) { + printJobs = new ArrayList<PrintJobInfo>(); + } + printJobs.addAll(bucket); + } + } else { + List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); + if (bucket != null) { + if (printJobs == null) { + printJobs = new ArrayList<PrintJobInfo>(); + } + printJobs.addAll(bucket); + } + } + if (printJobs != null) { + return printJobs; + } + return Collections.emptyList(); + } + } + + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + String tab = " "; + final int bucketCount = mPrintJobsForRunningApp.size(); + for (int i = 0; i < bucketCount; i++) { + final int appId = mPrintJobsForRunningApp.keyAt(i); + pw.append(prefix).append("appId=" + appId).append(':').println(); + List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); + final int printJobCount = bucket.size(); + for (int j = 0; j < printJobCount; j++) { + PrintJobInfo printJob = bucket.get(j); + pw.append(prefix).append(tab).append(printJob.toString()).println(); + } + } + } + } } } diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java index beeb899e8e2c..2798104a99ac 100644 --- a/services/java/com/android/server/wm/DisplayContent.java +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -162,6 +162,7 @@ class DisplayContent { void moveStack(TaskStack stack, boolean toTop) { mStackHistory.remove(stack); mStackHistory.add(toTop ? mStackHistory.size() : 0, stack); + mService.moveStackWindowsLocked(stack); } public boolean isPrivate() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index e330f8b91916..80c50cc2b2ff 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -4715,7 +4715,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void moveStackWindowsLocked(TaskStack stack) { + void moveStackWindowsLocked(TaskStack stack) { DisplayContent displayContent = stack.getDisplayContent(); // First remove all of the windows from the list. @@ -4782,7 +4782,6 @@ public class WindowManagerService extends IWindowManager.Stub } stack.moveTaskToTop(task); displayContent.moveStack(stack, true); - moveStackWindowsLocked(stack); } } finally { Binder.restoreCallingIdentity(origId); @@ -10142,16 +10141,6 @@ public class WindowManagerService extends IWindowManager.Stub return mSafeMode; } - @Override - public void showAssistant() { - // TODO: What permission? - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) - != PackageManager.PERMISSION_GRANTED) { - return; - } - mPolicy.showAssistant(); - } - void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); mPolicy.dump(" ", pw, args); diff --git a/tests/TransitionTests/res/layout/crossfade_multiple.xml b/tests/TransitionTests/res/layout/crossfade_multiple.xml index ca32ecbe29fd..3e6d55162c81 100644 --- a/tests/TransitionTests/res/layout/crossfade_multiple.xml +++ b/tests/TransitionTests/res/layout/crossfade_multiple.xml @@ -33,6 +33,11 @@ android:onClick="changeTransitionType" android:id="@+id/textfade2" android:text="@string/textfade2"/> + <RadioButton android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="changeTransitionType" + android:id="@+id/textfade3" + android:text="@string/textfade3"/> </RadioGroup> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml index 9cf7a94ec4c5..e3cff489d0c2 100644 --- a/tests/TransitionTests/res/values/strings.xml +++ b/tests/TransitionTests/res/values/strings.xml @@ -55,6 +55,7 @@ <string name="reveal">Reveal</string> <string name="crossfade">Crossfade</string> <string name="inout">In/Out</string> - <string name="textfade1">T1</string> - <string name="textfade2">T2</string> + <string name="textfade1">CT</string> + <string name="textfade2">CTO</string> + <string name="textfade3">CTI</string> </resources> diff --git a/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java index a1ddd7456139..01d46b2f8669 100644 --- a/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java +++ b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java @@ -22,7 +22,7 @@ import android.view.ViewGroup; import android.transition.Scene; import android.transition.TransitionSet; import android.transition.ChangeBounds; -import android.transition.TextChange; +import android.transition.ChangeText; import android.transition.TransitionManager; public class ChangingText extends Activity { @@ -44,7 +44,7 @@ public class ChangingText extends Activity { mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.changing_text_2, this); mChanger = new TransitionSet().setOrdering(TransitionSet.ORDERING_TOGETHER); - mChanger.addTransition(new ChangeBounds()).addTransition(new TextChange()); + mChanger.addTransition(new ChangeBounds()).addTransition(new ChangeText()); mCurrentScene = mScene1; } diff --git a/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java index 85702fa53a9d..54c44e22d1b6 100644 --- a/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java +++ b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java @@ -23,7 +23,7 @@ import android.view.ViewGroup; import android.transition.Scene; import android.widget.Button; import android.transition.Fade; -import android.transition.TextChange; +import android.transition.ChangeText; import android.transition.TransitionSet; import android.transition.TransitionManager; @@ -51,7 +51,7 @@ public class ClippingText extends Activity { mChanger = new TransitionSet().setOrdering(TransitionSet.ORDERING_TOGETHER); ChangeBounds changeBounds = new ChangeBounds(); changeBounds.setResizeClip(true); - mChanger.addTransition(changeBounds).addTransition(new TextChange()); + mChanger.addTransition(changeBounds).addTransition(new ChangeText()); mCurrentScene = mScene1; } diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java index d784f75f546c..469ee8b28a9f 100644 --- a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java +++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java @@ -21,7 +21,7 @@ import android.transition.ChangeBounds; import android.view.View; import android.view.ViewGroup; import android.transition.Crossfade; -import android.transition.TextChange; +import android.transition.ChangeText; import android.transition.Transition; import android.transition.TransitionSet; import android.transition.TransitionManager; @@ -41,7 +41,7 @@ public class CrossfadeMultiple extends Activity { Button mButton; Crossfade mCrossfade; TransitionSet mCrossfadeGroup; - TransitionSet mTextChangeGroup1, mTextChangeGroup2; + TransitionSet mTextChangeGroup1, mTextChangeGroup2, mTextChangeGroup3; TransitionSet mInOutGroup; @Override @@ -74,18 +74,21 @@ public class CrossfadeMultiple extends Activity { mInOutGroup.addTransition(inOut).addTransition(changeBounds); mTextChangeGroup1 = new TransitionSet(); - TextChange textChangeInOut = new TextChange(); - textChangeInOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT_IN); - mTextChangeGroup1.addTransition(textChangeInOut).addTransition(new ChangeBounds()); + ChangeText changeTextInOut = new ChangeText(); + changeTextInOut.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); + mTextChangeGroup1.addTransition(changeTextInOut).addTransition(new ChangeBounds()); mTextChangeGroup2 = new TransitionSet(); mTextChangeGroup2.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); - TextChange textChangeOut = new TextChange(); - textChangeOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT); - TextChange textChangeIn = new TextChange(); - textChangeIn.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_IN); - mTextChangeGroup2.addTransition(textChangeOut).addTransition(new ChangeBounds()). - addTransition(textChangeIn); + ChangeText changeTextOut = new ChangeText(); + changeTextOut.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT); + mTextChangeGroup2.addTransition(changeTextOut).addTransition(new ChangeBounds()); + + mTextChangeGroup3 = new TransitionSet(); + mTextChangeGroup3.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); + ChangeText changeTextIn = new ChangeText(); + changeTextIn.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_IN); + mTextChangeGroup3.addTransition(changeTextIn).addTransition(new ChangeBounds()); } public void sendMessage(View view) { @@ -135,6 +138,9 @@ public class CrossfadeMultiple extends Activity { case R.id.textfade2: mTransition = mTextChangeGroup2; break; + case R.id.textfade3: + mTransition = mTextChangeGroup3; + break; } } } diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 1e3b05899d08..632efe04f3c2 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -346,6 +346,8 @@ enum { LABEL_ATTR = 0x01010001, ICON_ATTR = 0x01010002, NAME_ATTR = 0x01010003, + PERMISSION_ATTR = 0x01010006, + RESOURCE_ATTR = 0x01010025, DEBUGGABLE_ATTR = 0x0101000f, VERSION_CODE_ATTR = 0x0101021b, VERSION_NAME_ATTR = 0x0101021c, @@ -372,6 +374,7 @@ enum { COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, PUBLIC_KEY_ATTR = 0x010103a6, + CATEGORY_ATTR = 0x010103e8, }; const char *getComponentName(String8 &pkgName, String8 &componentName) { @@ -424,6 +427,61 @@ static void printCompatibleScreens(ResXMLTree& tree) { printf("\n"); } +Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, + String8 *outError = NULL) +{ + Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER); + if (aidAsset == NULL) { + if (outError != NULL) *outError = "xml resource does not exist"; + return Vector<String8>(); + } + + const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service"); + + bool withinApduService = false; + Vector<String8> categories; + + String8 error; + ResXMLTree tree; + tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength()); + + size_t len; + int depth = 0; + ResXMLTree::event_code_t code; + while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code == ResXMLTree::END_TAG) { + depth--; + String8 tag(tree.getElementName(&len)); + + if (depth == 0 && tag == serviceTagName) { + withinApduService = false; + } + + } else if (code == ResXMLTree::START_TAG) { + depth++; + String8 tag(tree.getElementName(&len)); + + if (depth == 1) { + if (tag == serviceTagName) { + withinApduService = true; + } + } else if (depth == 2 && withinApduService) { + if (tag == "aid-group") { + String8 category = getAttribute(tree, CATEGORY_ATTR, &error); + if (error != "") { + if (outError != NULL) *outError = error; + return Vector<String8>(); + } + + categories.add(category); + } + } + } + } + aidAsset->close(); + return categories; +} + /* * Handle the "dump" command, to extract select data from an archive. */ @@ -631,12 +689,31 @@ int doDump(Bundle* bundle) bool hasOtherServices = false; bool hasWallpaperService = false; bool hasImeService = false; + bool hasAccessibilityService = false; + bool hasPrintService = false; bool hasWidgetReceivers = false; + bool hasDeviceAdminReceiver = false; bool hasIntentFilter = false; + bool hasPaymentService = false; bool actMainActivity = false; bool actWidgetReceivers = false; + bool actDeviceAdminEnabled = false; bool actImeService = false; bool actWallpaperService = false; + bool actAccessibilityService = false; + bool actPrintService = false; + bool actHostApduService = false; + bool actOffHostApduService = false; + bool hasMetaHostPaymentCategory = false; + bool hasMetaOffHostPaymentCategory = false; + + // These permissions are required by services implementing services + // the system binds to (IME, Accessibility, PrintServices, etc.) + bool hasBindDeviceAdminPermission = false; + bool hasBindInputMethodPermission = false; + bool hasBindAccessibilityServicePermission = false; + bool hasBindPrintServicePermission = false; + bool hasBindNfcServicePermission = false; // These two implement the implicit permissions that are granted // to pre-1.6 applications. @@ -747,6 +824,13 @@ int doDump(Bundle* bundle) hasOtherActivities |= withinActivity; hasOtherReceivers |= withinReceiver; hasOtherServices |= withinService; + } else { + if (withinService) { + hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory && + hasBindNfcServicePermission); + hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory && + hasBindNfcServicePermission); + } } withinActivity = false; withinService = false; @@ -760,11 +844,18 @@ int doDump(Bundle* bundle) hasOtherActivities |= !actMainActivity; } else if (withinReceiver) { hasWidgetReceivers |= actWidgetReceivers; - hasOtherReceivers |= !actWidgetReceivers; + hasDeviceAdminReceiver |= (actDeviceAdminEnabled && + hasBindDeviceAdminPermission); + hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled); } else if (withinService) { hasImeService |= actImeService; hasWallpaperService |= actWallpaperService; - hasOtherServices |= (!actImeService && !actWallpaperService); + hasAccessibilityService |= (actAccessibilityService && + hasBindAccessibilityServicePermission); + hasPrintService |= (actPrintService && hasBindPrintServicePermission); + hasOtherServices |= (!actImeService && !actWallpaperService && + !actAccessibilityService && !actPrintService && + !actHostApduService && !actOffHostApduService); } } withinIntentFilter = false; @@ -1109,6 +1200,13 @@ int doDump(Bundle* bundle) withinReceiver = false; withinService = false; hasIntentFilter = false; + hasMetaHostPaymentCategory = false; + hasMetaOffHostPaymentCategory = false; + hasBindDeviceAdminPermission = false; + hasBindInputMethodPermission = false; + hasBindAccessibilityServicePermission = false; + hasBindPrintServicePermission = false; + hasBindNfcServicePermission = false; if (withinApplication) { if(tag == "activity") { withinActivity = true; @@ -1166,6 +1264,16 @@ int doDump(Bundle* bundle) " %s\n", error.string()); goto bail; } + + String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + if (error == "") { + if (permission == "android.permission.BIND_DEVICE_ADMIN") { + hasBindDeviceAdminPermission = true; + } + } else { + fprintf(stderr, "ERROR getting 'android:permission' attribute for" + " receiver '%s': %s\n", receiverName.string(), error.string()); + } } else if (tag == "service") { withinService = true; serviceName = getAttribute(tree, NAME_ATTR, &error); @@ -1175,6 +1283,22 @@ int doDump(Bundle* bundle) " service: %s\n", error.string()); goto bail; } + + String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + if (error == "") { + if (permission == "android.permission.BIND_INPUT_METHOD") { + hasBindInputMethodPermission = true; + } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") { + hasBindAccessibilityServicePermission = true; + } else if (permission == "android.permission.BIND_PRINT_SERVICE") { + hasBindPrintServicePermission = true; + } else if (permission == "android.permission.BIND_NFC_SERVICE") { + hasBindNfcServicePermission = true; + } + } else { + fprintf(stderr, "ERROR getting 'android:permission' attribute for" + " service '%s': %s\n", serviceName.string(), error.string()); + } } } else if (withinSupportsInput && tag == "input-type") { String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -1186,10 +1310,60 @@ int doDump(Bundle* bundle) goto bail; } } - } else if ((depth == 4) && (tag == "intent-filter")) { - hasIntentFilter = true; - withinIntentFilter = true; - actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false; + } else if (depth == 4) { + if (tag == "intent-filter") { + hasIntentFilter = true; + withinIntentFilter = true; + actMainActivity = false; + actWidgetReceivers = false; + actImeService = false; + actWallpaperService = false; + actAccessibilityService = false; + actPrintService = false; + actDeviceAdminEnabled = false; + actHostApduService = false; + actOffHostApduService = false; + } else if (withinService && tag == "meta-data") { + String8 name = getAttribute(tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute for" + " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + goto bail; + } + + if (name == "android.nfc.cardemulation.host_apdu_service" || + name == "android.nfc.cardemulation.off_host_apdu_service") { + bool offHost = true; + if (name == "android.nfc.cardemulation.host_apdu_service") { + offHost = false; + } + + String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:resource' attribute for" + " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); + goto bail; + } + + Vector<String8> categories = getNfcAidCategories(assets, xmlPath, + offHost, &error); + if (error != "") { + fprintf(stderr, "ERROR getting AID category for service '%s'\n", + serviceName.string()); + goto bail; + } + + const size_t catLen = categories.size(); + for (size_t i = 0; i < catLen; i++) { + bool paymentCategory = (categories[i] == "payment"); + if (offHost) { + hasMetaOffHostPaymentCategory |= paymentCategory; + } else { + hasMetaHostPaymentCategory |= paymentCategory; + } + } + } + } } else if ((depth == 5) && withinIntentFilter){ String8 action; if (tag == "action") { @@ -1206,12 +1380,22 @@ int doDump(Bundle* bundle) } else if (withinReceiver) { if (action == "android.appwidget.action.APPWIDGET_UPDATE") { actWidgetReceivers = true; + } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") { + actDeviceAdminEnabled = true; } } else if (withinService) { if (action == "android.view.InputMethod") { actImeService = true; } else if (action == "android.service.wallpaper.WallpaperService") { actWallpaperService = true; + } else if (action == "android.accessibilityservice.AccessibilityService") { + actAccessibilityService = true; + } else if (action == "android.printservice.PrintService") { + actPrintService = true; + } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") { + actHostApduService = true; + } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") { + actOffHostApduService = true; } } if (action == "android.intent.action.SEARCH") { @@ -1411,12 +1595,24 @@ int doDump(Bundle* bundle) if (hasWidgetReceivers) { printf("app-widget\n"); } + if (hasDeviceAdminReceiver) { + printf("device-admin\n"); + } if (hasImeService) { printf("ime\n"); } if (hasWallpaperService) { printf("wallpaper\n"); } + if (hasAccessibilityService) { + printf("accessibility\n"); + } + if (hasPrintService) { + printf("print\n"); + } + if (hasPaymentService) { + printf("payment\n"); + } if (hasOtherActivities) { printf("other-activities\n"); } diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index f0c3a758fa3e..fd7a645647e0 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -458,11 +458,6 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void showAssistant() { - - } - - @Override public IBinder getFocusedWindowToken() { // TODO Auto-generated method stub return null; diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 76bd636e127f..798bc2e0c61c 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -1456,9 +1456,6 @@ public class WifiStateMachine extends StateMachine { public void setCountryCode(String countryCode, boolean persist) { if (persist) { mPersistedCountryCode = countryCode; - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.WIFI_COUNTRY_CODE, - countryCode); } sendMessage(CMD_SET_COUNTRY_CODE, countryCode); mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, countryCode); @@ -1691,8 +1688,8 @@ public class WifiStateMachine extends StateMachine { * Set the country code from the system setting value, if any. */ private void setCountryCode() { - String countryCode = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.WIFI_COUNTRY_CODE); + String countryCode = mContext.getResources().getString( + R.string.config_wifi_default_country_code); if (countryCode != null && !countryCode.isEmpty()) { setCountryCode(countryCode, false); } else { @@ -2417,13 +2414,6 @@ public class WifiStateMachine extends StateMachine { case CMD_BOOT_COMPLETED: String countryCode = mPersistedCountryCode; if (TextUtils.isEmpty(countryCode) == false) { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.WIFI_COUNTRY_CODE, - countryCode); - // it may be that the state transition that should send this info - // to the driver happened between mPersistedCountryCode getting set - // and now, so simply persisting it here would mean we have sent - // nothing to the driver. Send the cmd so it might be set now. sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode); } break; diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index 625ffb8733d4..50e8e3decf68 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -2545,12 +2545,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mServiceTransactionId = 0; mServiceDiscReqId = null; - String countryCode = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.WIFI_COUNTRY_CODE); - if (countryCode != null && !countryCode.isEmpty()) { - mP2pStateMachine.sendMessage(SET_COUNTRY_CODE, countryCode); - } - updatePersistentNetworks(RELOAD); } |