diff options
395 files changed, 9081 insertions, 3643 deletions
diff --git a/Android.mk b/Android.mk index d3e1cf5b4db8..c1c74ea04e55 100644 --- a/Android.mk +++ b/Android.mk @@ -226,7 +226,6 @@ LOCAL_SRC_FILES += \ core/java/android/service/carrier/ICarrierMessagingService.aidl \ core/java/android/service/gatekeeper/IGateKeeperService.aidl \ core/java/android/service/notification/INotificationListener.aidl \ - core/java/android/service/notification/INotificationAssistant.aidl \ core/java/android/service/notification/IStatusBarNotificationHolder.aidl \ core/java/android/service/notification/IConditionListener.aidl \ core/java/android/service/notification/IConditionProvider.aidl \ @@ -262,7 +261,7 @@ LOCAL_SRC_FILES += \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ core/java/android/view/IAssetAtlas.aidl \ - core/java/android/view/IDockDividerVisibilityListener.aidl \ + core/java/android/view/IDockedStackListener.aidl \ core/java/android/view/IGraphicsStats.aidl \ core/java/android/view/IInputFilter.aidl \ core/java/android/view/IInputFilterHost.aidl \ diff --git a/api/current.txt b/api/current.txt index 0aaa46fe407b..4eb8c4753155 100644 --- a/api/current.txt +++ b/api/current.txt @@ -67,7 +67,6 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -4082,11 +4081,14 @@ package android.app { } public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { + ctor public DatePickerDialog(android.content.Context); + ctor public DatePickerDialog(android.content.Context, int); ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); method public android.widget.DatePicker getDatePicker(); method public void onClick(android.content.DialogInterface, int); method public void onDateChanged(android.widget.DatePicker, int, int, int); + method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener); method public void updateDate(int, int, int); } @@ -5195,6 +5197,7 @@ package android.app { field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 field public final int priorityCallSenders; field public final int priorityCategories; field public final int priorityMessageSenders; @@ -9572,8 +9575,8 @@ package android.content.pm { field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; field public static final int GET_ACTIVITIES = 1; // 0x1 field public static final int GET_CONFIGURATIONS = 16384; // 0x4000 - field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200 - field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200 + field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 field public static final int GET_GIDS = 256; // 0x100 field public static final int GET_INSTRUMENTATION = 16; // 0x10 field public static final int GET_INTENT_FILTERS = 32; // 0x20 @@ -9585,13 +9588,17 @@ package android.content.pm { field public static final int GET_SERVICES = 4; // 0x4 field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400 field public static final int GET_SIGNATURES = 64; // 0x40 - field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 + field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800 field public static final int MATCH_ALL = 131072; // 0x20000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 + field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200 + field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000 - field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000 + field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 + field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000 + field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 @@ -11268,14 +11275,14 @@ package android.graphics { public static class BitmapFactory.Options { ctor public BitmapFactory.Options(); - method public void requestCancelDecode(); + method public deprecated void requestCancelDecode(); field public android.graphics.Bitmap inBitmap; field public int inDensity; - field public boolean inDither; + field public deprecated boolean inDither; field public deprecated boolean inInputShareable; field public boolean inJustDecodeBounds; field public boolean inMutable; - field public boolean inPreferQualityOverSpeed; + field public deprecated boolean inPreferQualityOverSpeed; field public android.graphics.Bitmap.Config inPreferredConfig; field public boolean inPremultiplied; field public deprecated boolean inPurgeable; @@ -18244,7 +18251,6 @@ package android.icu.util { method public static java.lang.String getCanonicalID(java.lang.String, boolean[]); method public int getDSTSavings(); method public static android.icu.util.TimeZone getDefault(); - method public static int getDefaultTimeZoneType(); method public final java.lang.String getDisplayName(); method public final java.lang.String getDisplayName(java.util.Locale); method public final java.lang.String getDisplayName(android.icu.util.ULocale); @@ -18268,8 +18274,6 @@ package android.icu.util { method public abstract boolean inDaylightTime(java.util.Date); method public boolean isFrozen(); method public boolean observesDaylightTime(); - method public static synchronized void setDefault(android.icu.util.TimeZone); - method public static synchronized void setDefaultTimeZoneType(int); method public void setID(java.lang.String); method public abstract void setRawOffset(int); method public abstract boolean useDaylightTime(); @@ -18282,8 +18286,6 @@ package android.icu.util { field public static final int SHORT_COMMONLY_USED = 6; // 0x6 field public static final int SHORT_GENERIC = 2; // 0x2 field public static final int SHORT_GMT = 4; // 0x4 - field public static final int TIMEZONE_ICU = 0; // 0x0 - field public static final int TIMEZONE_JDK = 1; // 0x1 field public static final android.icu.util.TimeZone UNKNOWN_ZONE; field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown"; } @@ -18379,8 +18381,6 @@ package android.icu.util { method public static java.lang.String getVariant(java.lang.String); method public boolean isRightToLeft(); method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale); method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String); method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String); method public java.lang.String toLanguageTag(); @@ -33103,6 +33103,7 @@ package android.security { public class NetworkSecurityPolicy { method public static android.security.NetworkSecurityPolicy getInstance(); method public boolean isCleartextTrafficPermitted(); + method public boolean isCleartextTrafficPermitted(java.lang.String); } } @@ -33495,20 +33496,13 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } - public class NotificationAdjustment implements android.os.Parcelable { - ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR; - } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { ctor public NotificationAssistantService(); - method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment); + method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); method public final void clearAnnotation(java.lang.String); method public void onNotificationActionClick(java.lang.String, long, int); method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); method public void onNotificationRemoved(java.lang.String, long, int); method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); method public final void setAnnotation(java.lang.String, android.app.Notification); @@ -33528,6 +33522,10 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; } + public class NotificationAssistantService.Adjustment { + ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri); + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -33561,6 +33559,7 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 } public static class NotificationListenerService.Ranking { @@ -33632,10 +33631,13 @@ package android.service.quicksettings { method public void onClick(); method public void onStartListening(); method public void onStopListening(); - method public void onTileAdded(); + method public int onTileAdded(); method public void onTileRemoved(); + method public static final void requestListeningState(android.content.Context, android.content.ComponentName); method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + field public static final int TILE_MODE_ACTIVE = 2; // 0x2 + field public static final int TILE_MODE_PASSIVE = 1; // 0x1 } } @@ -34896,6 +34898,7 @@ package android.telecom { field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_WIFI = 8; // 0x8 + field public static final int PROPERTY_WORK_CALL = 32; // 0x20 } public final class CallAudioState implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 9d4117ab75ad..fd946795d3c2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -93,7 +93,6 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT"; field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK"; field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; @@ -4202,11 +4201,14 @@ package android.app { } public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { + ctor public DatePickerDialog(android.content.Context); + ctor public DatePickerDialog(android.content.Context, int); ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); method public android.widget.DatePicker getDatePicker(); method public void onClick(android.content.DialogInterface, int); method public void onDateChanged(android.widget.DatePicker, int, int, int); + method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener); method public void updateDate(int, int, int); } @@ -5315,6 +5317,7 @@ package android.app { field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 field public final int priorityCallSenders; field public final int priorityCategories; field public final int priorityMessageSenders; @@ -9551,12 +9554,6 @@ package android.content.pm { method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); } - public class ManifestDigest implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.content.pm.ManifestDigest> CREATOR; - } - public class PackageInfo implements android.os.Parcelable { ctor public PackageInfo(); method public int describeContents(); @@ -9888,8 +9885,8 @@ package android.content.pm { field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; field public static final int GET_ACTIVITIES = 1; // 0x1 field public static final int GET_CONFIGURATIONS = 16384; // 0x4000 - field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200 - field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200 + field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 field public static final int GET_GIDS = 256; // 0x100 field public static final int GET_INSTRUMENTATION = 16; // 0x10 field public static final int GET_INTENT_FILTERS = 32; // 0x20 @@ -9901,7 +9898,7 @@ package android.content.pm { field public static final int GET_SERVICES = 4; // 0x4 field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400 field public static final int GET_SIGNATURES = 64; // 0x40 - field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 + field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800 field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3 @@ -9942,9 +9939,13 @@ package android.content.pm { field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ALL = 131072; // 0x20000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 + field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200 + field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000 - field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000 + field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 + field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000 + field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 @@ -11628,14 +11629,14 @@ package android.graphics { public static class BitmapFactory.Options { ctor public BitmapFactory.Options(); - method public void requestCancelDecode(); + method public deprecated void requestCancelDecode(); field public android.graphics.Bitmap inBitmap; field public int inDensity; - field public boolean inDither; + field public deprecated boolean inDither; field public deprecated boolean inInputShareable; field public boolean inJustDecodeBounds; field public boolean inMutable; - field public boolean inPreferQualityOverSpeed; + field public deprecated boolean inPreferQualityOverSpeed; field public android.graphics.Bitmap.Config inPreferredConfig; field public boolean inPremultiplied; field public deprecated boolean inPurgeable; @@ -19231,7 +19232,6 @@ package android.icu.util { method public static java.lang.String getCanonicalID(java.lang.String, boolean[]); method public int getDSTSavings(); method public static android.icu.util.TimeZone getDefault(); - method public static int getDefaultTimeZoneType(); method public final java.lang.String getDisplayName(); method public final java.lang.String getDisplayName(java.util.Locale); method public final java.lang.String getDisplayName(android.icu.util.ULocale); @@ -19255,8 +19255,6 @@ package android.icu.util { method public abstract boolean inDaylightTime(java.util.Date); method public boolean isFrozen(); method public boolean observesDaylightTime(); - method public static synchronized void setDefault(android.icu.util.TimeZone); - method public static synchronized void setDefaultTimeZoneType(int); method public void setID(java.lang.String); method public abstract void setRawOffset(int); method public abstract boolean useDaylightTime(); @@ -19269,8 +19267,6 @@ package android.icu.util { field public static final int SHORT_COMMONLY_USED = 6; // 0x6 field public static final int SHORT_GENERIC = 2; // 0x2 field public static final int SHORT_GMT = 4; // 0x4 - field public static final int TIMEZONE_ICU = 0; // 0x0 - field public static final int TIMEZONE_JDK = 1; // 0x1 field public static final android.icu.util.TimeZone UNKNOWN_ZONE; field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown"; } @@ -19366,8 +19362,6 @@ package android.icu.util { method public static java.lang.String getVariant(java.lang.String); method public boolean isRightToLeft(); method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale); method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String); method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String); method public java.lang.String toLanguageTag(); @@ -35245,6 +35239,7 @@ package android.security { public class NetworkSecurityPolicy { method public static android.security.NetworkSecurityPolicy getInstance(); method public boolean isCleartextTrafficPermitted(); + method public boolean isCleartextTrafficPermitted(java.lang.String); } } @@ -35637,20 +35632,13 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } - public class NotificationAdjustment implements android.os.Parcelable { - ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR; - } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { ctor public NotificationAssistantService(); - method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment); + method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); method public final void clearAnnotation(java.lang.String); method public void onNotificationActionClick(java.lang.String, long, int); method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); method public void onNotificationRemoved(java.lang.String, long, int); method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); method public final void setAnnotation(java.lang.String, android.app.Notification); @@ -35670,6 +35658,10 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; } + public class NotificationAssistantService.Adjustment { + ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri); + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -35708,6 +35700,7 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 field public static final int TRIM_FULL = 0; // 0x0 field public static final int TRIM_LIGHT = 1; // 0x1 } @@ -35806,10 +35799,13 @@ package android.service.quicksettings { method public void onClick(); method public void onStartListening(); method public void onStopListening(); - method public void onTileAdded(); + method public int onTileAdded(); method public void onTileRemoved(); + method public static final void requestListeningState(android.content.Context, android.content.ComponentName); method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + field public static final int TILE_MODE_ACTIVE = 2; // 0x2 + field public static final int TILE_MODE_PASSIVE = 1; // 0x1 } } @@ -37113,6 +37109,7 @@ package android.telecom { field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_WIFI = 8; // 0x8 + field public static final int PROPERTY_WORK_CALL = 32; // 0x20 } public static abstract deprecated class Call.Listener extends android.telecom.Call.Callback { diff --git a/api/test-current.txt b/api/test-current.txt index 6ee1eb86551e..2b6077bed84d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -67,7 +67,6 @@ package android { field public static final java.lang.String DUMP = "android.permission.DUMP"; field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST"; - field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT"; field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE"; @@ -4082,11 +4081,14 @@ package android.app { } public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { + ctor public DatePickerDialog(android.content.Context); + ctor public DatePickerDialog(android.content.Context, int); ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); method public android.widget.DatePicker getDatePicker(); method public void onClick(android.content.DialogInterface, int); method public void onDateChanged(android.widget.DatePicker, int, int, int); + method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener); method public void updateDate(int, int, int); } @@ -5195,6 +5197,7 @@ package android.app { field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 field public final int priorityCallSenders; field public final int priorityCategories; field public final int priorityMessageSenders; @@ -9572,8 +9575,8 @@ package android.content.pm { field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; field public static final int GET_ACTIVITIES = 1; // 0x1 field public static final int GET_CONFIGURATIONS = 16384; // 0x4000 - field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200 - field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200 + field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 field public static final int GET_GIDS = 256; // 0x100 field public static final int GET_INSTRUMENTATION = 16; // 0x10 field public static final int GET_INTENT_FILTERS = 32; // 0x20 @@ -9585,13 +9588,17 @@ package android.content.pm { field public static final int GET_SERVICES = 4; // 0x4 field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400 field public static final int GET_SIGNATURES = 64; // 0x40 - field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 + field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800 field public static final int MATCH_ALL = 131072; // 0x20000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 + field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200 + field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 + field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000 field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000 - field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000 - field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000 + field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000 + field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000 + field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L field public static final int PERMISSION_DENIED = -1; // 0xffffffff field public static final int PERMISSION_GRANTED = 0; // 0x0 @@ -11268,14 +11275,14 @@ package android.graphics { public static class BitmapFactory.Options { ctor public BitmapFactory.Options(); - method public void requestCancelDecode(); + method public deprecated void requestCancelDecode(); field public android.graphics.Bitmap inBitmap; field public int inDensity; - field public boolean inDither; + field public deprecated boolean inDither; field public deprecated boolean inInputShareable; field public boolean inJustDecodeBounds; field public boolean inMutable; - field public boolean inPreferQualityOverSpeed; + field public deprecated boolean inPreferQualityOverSpeed; field public android.graphics.Bitmap.Config inPreferredConfig; field public boolean inPremultiplied; field public deprecated boolean inPurgeable; @@ -18244,7 +18251,6 @@ package android.icu.util { method public static java.lang.String getCanonicalID(java.lang.String, boolean[]); method public int getDSTSavings(); method public static android.icu.util.TimeZone getDefault(); - method public static int getDefaultTimeZoneType(); method public final java.lang.String getDisplayName(); method public final java.lang.String getDisplayName(java.util.Locale); method public final java.lang.String getDisplayName(android.icu.util.ULocale); @@ -18268,8 +18274,6 @@ package android.icu.util { method public abstract boolean inDaylightTime(java.util.Date); method public boolean isFrozen(); method public boolean observesDaylightTime(); - method public static synchronized void setDefault(android.icu.util.TimeZone); - method public static synchronized void setDefaultTimeZoneType(int); method public void setID(java.lang.String); method public abstract void setRawOffset(int); method public abstract boolean useDaylightTime(); @@ -18282,8 +18286,6 @@ package android.icu.util { field public static final int SHORT_COMMONLY_USED = 6; // 0x6 field public static final int SHORT_GENERIC = 2; // 0x2 field public static final int SHORT_GMT = 4; // 0x4 - field public static final int TIMEZONE_ICU = 0; // 0x0 - field public static final int TIMEZONE_JDK = 1; // 0x1 field public static final android.icu.util.TimeZone UNKNOWN_ZONE; field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown"; } @@ -18379,8 +18381,6 @@ package android.icu.util { method public static java.lang.String getVariant(java.lang.String); method public boolean isRightToLeft(); method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale); - method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale); method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String); method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String); method public java.lang.String toLanguageTag(); @@ -33106,6 +33106,7 @@ package android.security { public class NetworkSecurityPolicy { method public static android.security.NetworkSecurityPolicy getInstance(); method public boolean isCleartextTrafficPermitted(); + method public boolean isCleartextTrafficPermitted(java.lang.String); } } @@ -33498,20 +33499,13 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService"; } - public class NotificationAdjustment implements android.os.Parcelable { - ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR; - } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { ctor public NotificationAssistantService(); - method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment); + method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment); method public final void clearAnnotation(java.lang.String); method public void onNotificationActionClick(java.lang.String, long, int); method public void onNotificationClick(java.lang.String, long); - method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); + method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean); method public void onNotificationRemoved(java.lang.String, long, int); method public void onNotificationVisibilityChanged(java.lang.String, long, boolean); method public final void setAnnotation(java.lang.String, android.app.Notification); @@ -33531,6 +33525,10 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; } + public class NotificationAssistantService.Adjustment { + ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri); + } + public abstract class NotificationListenerService extends android.app.Service { ctor public NotificationListenerService(); method public final void cancelAllNotifications(); @@ -33564,6 +33562,7 @@ package android.service.notification { field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1 field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2 + field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4 } public static class NotificationListenerService.Ranking { @@ -33635,10 +33634,13 @@ package android.service.quicksettings { method public void onClick(); method public void onStartListening(); method public void onStopListening(); - method public void onTileAdded(); + method public int onTileAdded(); method public void onTileRemoved(); + method public static final void requestListeningState(android.content.Context, android.content.ComponentName); method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + field public static final int TILE_MODE_ACTIVE = 2; // 0x2 + field public static final int TILE_MODE_PASSIVE = 1; // 0x1 } } @@ -34899,6 +34901,7 @@ package android.telecom { field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_WIFI = 8; // 0x8 + field public static final int PROPERTY_WORK_CALL = 32; // 0x20 } public final class CallAudioState implements android.os.Parcelable { diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 4f7a1095cfbe..6302d7418cd0 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -159,6 +159,7 @@ public class Am extends BaseCommand { " am stack start <DISPLAY_ID> <INTENT>\n" + " am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" + " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" + + " am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]\n" + " am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" + " am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" + " am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" + @@ -299,7 +300,11 @@ public class Am extends BaseCommand { "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" + " bottom (false) of <STACK_ID>.\n" + "\n" + - "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>." + + "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" + + "\n" + + "am stack resize-docked-stack: change docked stack to <LEFT,TOP,RIGHT,BOTTOM>\n" + + " and supplying temporary different task bounds indicated by\n" + + " <TASK_LEFT,TOP,RIGHT,BOTTOM>\n" + "\n" + "am stack size-docked-stack-test: test command for sizing docked stack by\n" + " <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" + @@ -1683,6 +1688,9 @@ public class Am extends BaseCommand { case "resize": runStackResize(); break; + case "resize-docked-stack": + runStackResizeDocked(); + break; case "positiontask": runStackPositionTask(); break; @@ -1751,6 +1759,20 @@ public class Am extends BaseCommand { resizeStack(stackId, bounds, 0); } + private void runStackResizeDocked() throws Exception { + final Rect bounds = getBounds(); + final Rect taskBounds = getBounds(); + if (bounds == null || taskBounds == null) { + System.err.println("Error: invalid input bounds"); + return; + } + try { + mAm.resizeDockedStack(bounds, taskBounds, null, null, null); + } catch (RemoteException e) { + showError("Error: resizing docked stack " + e); + } + } + private void resizeStack(int stackId, Rect bounds, int delayMs) throws Exception { if (bounds == null) { showError("Error: invalid input bounds"); diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 3e6b595da00e..6fc0d74b5f18 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -1082,6 +1082,10 @@ public abstract class ActionBar { return false; } + /** @hide */ + public void onDestroy() { + } + /** * Common implementation for requestFocus that takes in the Toolbar and moves focus * to the contents. This makes the ViewGroups containing the toolbar allow focus while it stays diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 800ebc6ce243..8346161548aa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1703,6 +1703,10 @@ public class Activity extends ContextThemeWrapper mSearchManager.stopSearch(); } + if (mActionBar != null) { + mActionBar.onDestroy(); + } + getApplication().dispatchActivityDestroyed(this); } @@ -2208,14 +2212,22 @@ public class Activity extends ContextThemeWrapper * @param toolbar Toolbar to set as the Activity's action bar */ public void setActionBar(@Nullable Toolbar toolbar) { - if (getActionBar() instanceof WindowDecorActionBar) { + final ActionBar ab = getActionBar(); + if (ab instanceof WindowDecorActionBar) { throw new IllegalStateException("This Activity already has an action bar supplied " + "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " + "android:windowActionBar to false in your theme to use a Toolbar instead."); } - // Clear out the MenuInflater to make sure that it is valid for the new Action Bar + + // If we reach here then we're setting a new action bar + // First clear out the MenuInflater to make sure that it is valid for the new Action Bar mMenuInflater = null; + // If we have an action bar currently, destroy it + if (ab != null) { + ab.onDestroy(); + } + ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this); mActionBar = tbab; mWindow.setCallback(tbab.getWrappedWindowCallback()); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 3a3aa92697e6..da21099eda2b 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -332,10 +332,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM { data.enforceInterface(IActivityManager.descriptor); final int taskId = data.readInt(); - final int launchStackId = data.readInt(); final Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data); - final int result = startActivityFromRecents(taskId, launchStackId, options); + final int result = startActivityFromRecents(taskId, options); reply.writeNoException(); reply.writeInt(result); return true; @@ -571,6 +570,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case ACTIVITY_RELAUNCHED_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + activityRelaunched(token); + reply.writeNoException(); + return true; + } + case GET_CALLING_PACKAGE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -786,6 +793,39 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case RESIZE_DOCKED_STACK_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final boolean hasBounds = data.readInt() != 0; + Rect bounds = null; + if (hasBounds) { + bounds = Rect.CREATOR.createFromParcel(data); + } + final boolean hasTempDockedTaskBounds = data.readInt() != 0; + Rect tempDockedTaskBounds = null; + if (hasTempDockedTaskBounds) { + tempDockedTaskBounds = Rect.CREATOR.createFromParcel(data); + } + final boolean hasTempDockedTaskInsetBounds = data.readInt() != 0; + Rect tempDockedTaskInsetBounds = null; + if (hasTempDockedTaskInsetBounds) { + tempDockedTaskInsetBounds = Rect.CREATOR.createFromParcel(data); + } + final boolean hasTempOtherTaskBounds = data.readInt() != 0; + Rect tempOtherTaskBounds = null; + if (hasTempOtherTaskBounds) { + tempOtherTaskBounds = Rect.CREATOR.createFromParcel(data); + } + final boolean hasTempOtherTaskInsetBounds = data.readInt() != 0; + Rect tempOtherTaskInsetBounds = null; + if (hasTempOtherTaskInsetBounds) { + tempOtherTaskInsetBounds = Rect.CREATOR.createFromParcel(data); + } + resizeDockedStack(bounds, tempDockedTaskBounds, tempDockedTaskInsetBounds, + tempOtherTaskBounds, tempOtherTaskInsetBounds); + reply.writeNoException(); + return true; + } + case POSITION_TASK_IN_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int taskId = data.readInt(); @@ -3088,13 +3128,12 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); return result != 0; } - public int startActivityFromRecents(int taskId, int launchStackId, Bundle options) + public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(taskId); - data.writeInt(launchStackId); if (options == null) { data.writeInt(0); } else { @@ -3382,6 +3421,17 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public void activityRelaunched(IBinder token) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(ACTIVITY_RELAUNCHED_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } public String getCallingPackage(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3692,6 +3742,50 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override + public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) + throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + if (dockedBounds != null) { + data.writeInt(1); + dockedBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + if (tempDockedTaskBounds != null) { + data.writeInt(1); + tempDockedTaskBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + if (tempDockedTaskInsetBounds != null) { + data.writeInt(1); + tempDockedTaskInsetBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + if (tempOtherTaskBounds != null) { + data.writeInt(1); + tempOtherTaskBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + if (tempOtherTaskInsetBounds != null) { + data.writeInt(1); + tempOtherTaskInsetBounds.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + mRemote.transact(RESIZE_DOCKED_STACK_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + @Override public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index cee1aa5b90e9..e596a68304fb 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -17,6 +17,7 @@ package android.app; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import android.content.Context; import android.content.Intent; @@ -67,7 +68,8 @@ public class ActivityOptions { * The bounds (window size) that the activity should be launched in. Set to null explicitly for * full screen. If the key is not found, previous bounds will be preserved. * NOTE: This value is ignored on devices that don't have - * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled. + * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or + * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled. * @hide */ public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds"; @@ -145,6 +147,12 @@ public class ActivityOptions { private static final String KEY_ANIM_SPECS = "android:activity.animSpecs"; /** + * The stack id the activity should be launched into. + * @hide + */ + private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId"; + + /** * Where the docked stack should be positioned. * @hide */ @@ -215,6 +223,7 @@ public class ActivityOptions { private int mResultCode; private int mExitCoordinatorIndex; private PendingIntent mUsageTimeReport; + private int mLaunchStackId = INVALID_STACK_ID; private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; private AppTransitionAnimationSpec mAnimSpecs[]; @@ -754,6 +763,7 @@ public class ActivityOptions { mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX); break; } + mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID); mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT); if (opts.containsKey(KEY_ANIM_SPECS)) { Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); @@ -901,7 +911,19 @@ public class ActivityOptions { } /** @hide */ - public int getDockCreateMode() { return mDockCreateMode; } + public int getLaunchStackId() { + return mLaunchStackId; + } + + /** @hide */ + public void setLaunchStackId(int launchStackId) { + mLaunchStackId = launchStackId; + } + + /** @hide */ + public int getDockCreateMode() { + return mDockCreateMode; + } /** @hide */ public void setDockCreateMode(int dockCreateMode) { @@ -1049,6 +1071,7 @@ public class ActivityOptions { b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex); break; } + b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId); b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode); if (mAnimSpecs != null) { b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ed168d1674ac..177fabe2d25c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4161,6 +4161,15 @@ public final class ActivityThread { r.pendingIntents = pendingNewIntents; } } + + // For each relaunch request, activity manager expects an answer + if (!r.onlyLocalRequest && fromServer) { + try { + ActivityManagerNative.getDefault().activityRelaunched(token); + } catch (RemoteException e) { + e.printStackTrace(); + } + } break; } } @@ -4275,6 +4284,13 @@ public final class ActivityThread { ActivityClientRecord r = mActivities.get(tmp.token); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r); if (r == null) { + if (!tmp.onlyLocalRequest) { + try { + ActivityManagerNative.getDefault().activityRelaunched(tmp.token); + } catch (RemoteException e) { + // If the system process has died, it's game over for everyone. + } + } return; } @@ -4320,6 +4336,14 @@ public final class ActivityThread { r.overrideConfig = tmp.overrideConfig; handleLaunchActivity(r, currentIntent); + + if (!tmp.onlyLocalRequest) { + try { + ActivityManagerNative.getDefault().activityRelaunched(r.token); + } catch (RemoteException e) { + // If the system process has died, it's game over for everyone. + } + } } private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 77721e687389..a60fbf6374c9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -42,7 +42,6 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; -import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageItemInfo; @@ -1421,7 +1420,7 @@ public class ApplicationPackageManager extends PackageManager { public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName) { final VerificationParams verificationParams = new VerificationParams(null, null, - null, VerificationParams.NO_UID, null); + null, VerificationParams.NO_UID); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, installerPackageName, verificationParams, null, mContext.getUserId()); } @@ -1429,9 +1428,9 @@ public class ApplicationPackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, - ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + ContainerEncryptionParams encryptionParams) { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, - null, VerificationParams.NO_UID, manifestDigest); + null, VerificationParams.NO_UID); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, installerPackageName, verificationParams, encryptionParams, mContext.getUserId()); } @@ -1455,7 +1454,7 @@ public class ApplicationPackageManager extends PackageManager { public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, int userId) { final VerificationParams verificationParams = new VerificationParams(null, null, - null, VerificationParams.NO_UID, null); + null, VerificationParams.NO_UID); installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null, userId); } @@ -1463,10 +1462,10 @@ public class ApplicationPackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, - Uri verificationURI, ManifestDigest manifestDigest, + Uri verificationURI, ContainerEncryptionParams encryptionParams) { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, - null, VerificationParams.NO_UID, manifestDigest); + null, VerificationParams.NO_UID); installCommon(packageURI, observer, flags, installerPackageName, verificationParams, encryptionParams, mContext.getUserId()); } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 3fbbdffba58f..8d7f347024b2 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -16,6 +16,9 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StyleRes; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -35,95 +38,126 @@ import java.util.Calendar; /** * A simple dialog containing an {@link android.widget.DatePicker}. - * - * <p>See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> - * guide.</p> + * <p> + * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> + * guide. */ public class DatePickerDialog extends AlertDialog implements OnClickListener, OnDateChangedListener { - private static final String YEAR = "year"; private static final String MONTH = "month"; private static final String DAY = "day"; private final DatePicker mDatePicker; - private final OnDateSetListener mDateSetListener; private final Calendar mCalendar; + private OnDateSetListener mDateSetListener; + private boolean mTitleNeedsUpdate = true; /** - * The callback used to indicate the user is done filling in the date. + * Creates a new date picker dialog for the current date using the parent + * context's default date picker dialog theme. + * + * @param context the parent context */ - public interface OnDateSetListener { - - /** - * @param view The view associated with this listener. - * @param year The year that was set. - * @param monthOfYear The month that was set (0-11) for compatibility - * with {@link java.util.Calendar}. - * @param dayOfMonth The day of the month that was set. - */ - void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth); + public DatePickerDialog(Context context) { + this(context, 0); } /** - * @param context The context the dialog is to run in. - * @param callBack How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param monthOfYear The initial month of the dialog. - * @param dayOfMonth The initial day of the dialog. - */ - public DatePickerDialog(Context context, - OnDateSetListener callBack, - int year, - int monthOfYear, - int dayOfMonth) { - this(context, 0, callBack, year, monthOfYear, dayOfMonth); - } - - static int resolveDialogTheme(Context context, int resid) { - if (resid == 0) { - final TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true); - return outValue.resourceId; - } else { - return resid; - } - } - - /** - * @param context The context the dialog is to run in. - * @param theme the theme to apply to this dialog - * @param listener How the parent is notified that the date is set. - * @param year The initial year of the dialog. - * @param monthOfYear The initial month of the dialog. - * @param dayOfMonth The initial day of the dialog. + * Creates a new date picker dialog for the current date. + * + * @param context the parent context + * @param themeResId the resource ID of the theme against which to inflate + * this dialog, or {@code 0} to use the parent + * {@code context}'s default alert dialog theme */ - public DatePickerDialog(Context context, int theme, OnDateSetListener listener, int year, - int monthOfYear, int dayOfMonth) { - super(context, resolveDialogTheme(context, theme)); - - mDateSetListener = listener; - mCalendar = Calendar.getInstance(); + public DatePickerDialog(Context context, @StyleRes int themeResId) { + super(context, resolveDialogTheme(context, themeResId)); final Context themeContext = getContext(); final LayoutInflater inflater = LayoutInflater.from(themeContext); final View view = inflater.inflate(R.layout.date_picker_dialog, null); setView(view); + setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this); setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this); setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); + mCalendar = Calendar.getInstance(); + + final int year = mCalendar.get(Calendar.YEAR); + final int monthOfYear = mCalendar.get(Calendar.MONTH); + final int dayOfMonth = mCalendar.get(Calendar.DAY_OF_MONTH); mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); mDatePicker.init(year, monthOfYear, dayOfMonth, this); mDatePicker.setValidationCallback(mValidationCallback); } + /** + * Creates a new date picker dialog for the specified date using the parent + * context's default date picker dialog theme. + * + * @param context the parent context + * @param listener the listener to call when the user sets the date + * @param year the initially selected year + * @param month the initially selected month (0-11 for compatibility with + * {@link Calendar#MONTH}) + * @param dayOfMonth the initially selected day of month (1-31, depending + * on month) + */ + public DatePickerDialog(@Nullable Context context, @Nullable OnDateSetListener listener, + int year, int month, int dayOfMonth) { + this(context, 0, listener, year, month, dayOfMonth); + } + + /** + * Creates a new date picker dialog for the specified date. + * + * @param context the parent context + * @param themeResId the resource ID of the theme against which to inflate + * this dialog, or {@code 0} to use the parent + * {@code context}'s default alert dialog theme + * @param listener the listener to call when the user sets the date + * @param year the initially selected year + * @param month the initially selected month (0-11 for compatibility with + * {@link Calendar#MONTH}) + * @param dayOfMonth the initially selected day of month (1-31, depending + * on month) + */ + public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId, + @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) { + this(context, themeResId); + + mDateSetListener = listener; + + mDatePicker.updateDate(year, month, dayOfMonth); + } + + static int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) { + if (themeResId == 0) { + final TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true); + return outValue.resourceId; + } else { + return themeResId; + } + } + @Override - public void onDateChanged(DatePicker view, int year, int month, int day) { - mDatePicker.init(year, month, day, this); - updateTitle(year, month, day); + public void onDateChanged(DatePicker view, int year, int month, int dayOfMonth) { + mDatePicker.init(year, month, dayOfMonth, this); + updateTitle(year, month, dayOfMonth); + } + + /** + * Sets the listener to call when the user sets the date. + * + * @param listener the listener to call when the user sets the date + */ + public void setOnDateSetListener(@Nullable OnDateSetListener listener) { + mDateSetListener = listener; } @Override @@ -145,10 +179,11 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, } /** - * Gets the {@link DatePicker} contained in this dialog. + * Returns the {@link DatePicker} contained in this dialog. * - * @return The calendar view. + * @return the date picker */ + @NonNull public DatePicker getDatePicker() { return mDatePicker; } @@ -156,20 +191,22 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, /** * Sets the current date. * - * @param year The date year. - * @param monthOfYear The date month. - * @param dayOfMonth The date day of month. + * @param year the year + * @param month the month (0-11 for compatibility with + * {@link Calendar#MONTH}) + * @param dayOfMonth the day of month (1-31, depending on month) */ - public void updateDate(int year, int monthOfYear, int dayOfMonth) { - mDatePicker.updateDate(year, monthOfYear, dayOfMonth); + public void updateDate(int year, int month, int dayOfMonth) { + mDatePicker.updateDate(year, month, dayOfMonth); } - private void updateTitle(int year, int month, int day) { + private void updateTitle(int year, int month, int dayOfMonth) { if (!mDatePicker.getCalendarViewShown()) { mCalendar.set(Calendar.YEAR, year); mCalendar.set(Calendar.MONTH, month); - mCalendar.set(Calendar.DAY_OF_MONTH, day); - String title = DateUtils.formatDateTime(mContext, + mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); + + final String title = DateUtils.formatDateTime(mContext, mCalendar.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY @@ -177,12 +214,12 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, | DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_ABBREV_WEEKDAY); setTitle(title); + mTitleNeedsUpdate = true; - } else { - if (mTitleNeedsUpdate) { - mTitleNeedsUpdate = false; - setTitle(R.string.date_picker_dialog_title); - } + } else if (mTitleNeedsUpdate) { + setTitle(R.string.date_picker_dialog_title); + + mTitleNeedsUpdate = false; } } @@ -213,4 +250,19 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, } } }; + + /** + * The listener used to indicate the user has finished selecting a date. + */ + public interface OnDateSetListener { + /** + * @param view the picker associated with the dialog + * @param year the selected year + * @param month the selected month (0-11 for compatibility with + * {@link Calendar#MONTH}) + * @param dayOfMonth th selected day of the month (1-31, depending on + * month) + */ + void onDateSet(DatePicker view, int year, int month, int dayOfMonth); + } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 61b1bc8f0546..ceb14ad6d5d2 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -92,7 +92,7 @@ public interface IActivityManager extends IInterface { int userId) throws RemoteException; public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent, Bundle options) throws RemoteException; - public int startActivityFromRecents(int taskId, int launchStackId, Bundle options) + public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException; public boolean finishActivity(IBinder token, int code, Intent data, int finishTask) throws RemoteException; @@ -122,6 +122,7 @@ public interface IActivityManager extends IInterface { PersistableBundle persistentState, CharSequence description) throws RemoteException; public void activitySlept(IBinder token) throws RemoteException; public void activityDestroyed(IBinder token) throws RemoteException; + public void activityRelaunched(IBinder token) throws RemoteException; public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, int[] verticalSizeConfigurations, int[] smallestWidthConfigurations) throws RemoteException; @@ -145,7 +146,31 @@ public interface IActivityManager extends IInterface { public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate, Rect initialBounds) throws RemoteException; public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException; - public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException; + public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) + throws RemoteException; + + /** + * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change. + * + * @param dockedBounds The bounds for the docked stack. + * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which + * might be different from the stack bounds to allow more + * flexibility while resizing, or {@code null} if they should be the + * same as the stack bounds. + * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets. + * When resizing, we usually "freeze" the layout of a task. To + * achieve that, we also need to "freeze" the insets, which + * gets achieved by changing task bounds but not bounds used + * to calculate the insets in this transient state + * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or + * {@code null} if they should be the same as the stack bounds. + * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other + * stacks. + * @throws RemoteException + */ + public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) throws RemoteException; public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException; public List<StackInfo> getAllStackInfos() throws RemoteException; public StackInfo getStackInfo(int stackId) throws RemoteException; @@ -920,6 +945,8 @@ public interface IActivityManager extends IInterface { int IN_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 353; int KILL_PACKAGE_DEPENDENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 354; int ENTER_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 355; - int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356; + int ACTIVITY_RELAUNCHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356; int GET_URI_PERMISSION_OWNER_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 357; + int RESIZE_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 358; + int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 359; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index abe9822e972f..e60cb0377e96 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -78,6 +78,8 @@ interface INotificationManager void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); void setInterruptionFilter(String pkg, int interruptionFilter); + void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation); + ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); boolean isSystemConditionProviderEnabled(String path); diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 4b0935ca23cc..560e22a3be1d 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -18,5 +18,9 @@ package android.app; /** @hide */ oneway interface ITaskStackListener { + /** Called whenever there are changes to the state of tasks in a stack. */ void onTaskStackChanged(); + + /** Called whenever an Activity is moved to the pinned stack from another stack. */ + void onActivityPinned(); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 85d9831921ac..9a3c82036399 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -637,10 +637,12 @@ public class NotificationManager public static final int SUPPRESSED_EFFECTS_UNSET = -1; public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 0; public static final int SUPPRESSED_EFFECT_PEEK = 1 << 1; + public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 2; private static final int[] ALL_SUPPRESSED_EFFECTS = { SUPPRESSED_EFFECT_LIGHTS, SUPPRESSED_EFFECT_PEEK, + SUPPRESSED_EFFECT_SCREEN_ON, }; /** @@ -750,6 +752,7 @@ public class NotificationManager switch (effect) { case SUPPRESSED_EFFECT_LIGHTS: return "SUPPRESSED_EFFECT_LIGHTS"; case SUPPRESSED_EFFECT_PEEK: return "SUPPRESSED_EFFECT_PEEK"; + case SUPPRESSED_EFFECT_SCREEN_ON: return "SUPPRESSED_EFFECT_SCREEN_ON"; case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET"; default: return "UNKNOWN_" + effect; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0f3ca10c8fa4..c3dfab48acf5 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4169,11 +4169,18 @@ public class Intent implements Parcelable, Cloneable { /** * Internal flag used to indicate that a system component has done their - * homework and verified their encryption-aware behavior. + * homework and verified that they correctly handle packages and components + * that come and go over time. In particular: + * <ul> + * <li>Apps installed on external storage, which will appear to be + * uninstalled while the the device is ejected. + * <li>Apps with encryption unaware components, which will appear to not + * exist while the device is locked. + * </ul> * * @hide */ - public static final int FLAG_DEBUG_ENCRYPTION_TRIAGED = 0x00000100; + public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100; /** * If set, the new activity is not kept in the history stack. As soon as diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 326735ec4f36..dedf07f5dcff 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -281,6 +281,29 @@ public class ActivityInfo extends ComponentInfo * {@see android.app.Activity#setVrMode(boolean)}. */ public static final int FLAG_ENABLE_VR_MODE = 0x8000; + + /** + * Bit in {@link #flags} indicating if the activity is resizeable to any dimension. + * See {@link android.R.attr#resizeableActivity}. + * @hide + */ + public static final int FLAG_RESIZEABLE = 0x10000; + + /** + * Bit in {@link #flags} indicating if the activity is supports picture-in-picture form of + * multi-window mode. See {@link android.R.attr#supportsPictureInPicture}. + * @hide + */ + public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x20000; + + /** + * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is + * in a task/stack whose activities are normally not focusable. + * See android.R.attr#alwaysFocusable. + * @hide + */ + public static final int FLAG_ALWAYS_FOCUSABLE = 0x40000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -670,20 +693,6 @@ public class ActivityInfo extends ComponentInfo */ public String parentActivityName; - /** - * Value indicating if the activity is resizeable to any dimension. - * See {@link android.R.attr#resizeableActivity}. - * @hide - */ - public boolean resizeable; - - /** - * Value indicating if the activity is supports picture-in-picture form of multi-window mode. - * See {@link android.R.attr#supportsPictureInPicture}. - * @hide - */ - public boolean supportsPip; - /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0; /** @hide */ @@ -735,8 +744,6 @@ public class ActivityInfo extends ComponentInfo uiOptions = orig.uiOptions; parentActivityName = orig.parentActivityName; maxRecents = orig.maxRecents; - resizeable = orig.resizeable; - supportsPip = orig.supportsPip; lockTaskLaunchMode = orig.lockTaskLaunchMode; layout = orig.layout; } @@ -791,7 +798,6 @@ public class ActivityInfo extends ComponentInfo pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions)); } if ((flags&DUMP_FLAG_DETAILS) != 0) { - pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip); pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode)); } @@ -829,8 +835,6 @@ public class ActivityInfo extends ComponentInfo dest.writeString(parentActivityName); dest.writeInt(persistableMode); dest.writeInt(maxRecents); - dest.writeInt(resizeable ? 1 : 0); - dest.writeInt(supportsPip ? 1 : 0); dest.writeInt(lockTaskLaunchMode); if (layout != null) { dest.writeInt(1); @@ -871,8 +875,6 @@ public class ActivityInfo extends ComponentInfo parentActivityName = source.readString(); persistableMode = source.readInt(); maxRecents = source.readInt(); - resizeable = (source.readInt() == 1); - supportsPip = (source.readInt() == 1); lockTaskLaunchMode = source.readInt(); if (source.readInt() == 1) { layout = new Layout(source); diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java deleted file mode 100644 index e7dc76488903..000000000000 --- a/core/java/android/content/pm/ManifestDigest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm; - -import com.android.internal.util.HexDump; - -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Slog; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import libcore.io.IoUtils; - -/** - * Represents the manifest digest for a package. This is suitable for comparison - * of two packages to know whether the manifests are identical. - * - * @hide - */ -@SystemApi -public class ManifestDigest implements Parcelable { - private static final String TAG = "ManifestDigest"; - - /** The digest of the manifest in our preferred order. */ - private final byte[] mDigest; - - /** What we print out first when toString() is called. */ - private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest="; - - /** Digest algorithm to use. */ - private static final String DIGEST_ALGORITHM = "SHA-256"; - - ManifestDigest(byte[] digest) { - mDigest = digest; - } - - private ManifestDigest(Parcel source) { - mDigest = source.createByteArray(); - } - - static ManifestDigest fromInputStream(InputStream fileIs) { - if (fileIs == null) { - return null; - } - - final MessageDigest md; - try { - md = MessageDigest.getInstance(DIGEST_ALGORITHM); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e); - } - - final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md); - try { - byte[] readBuffer = new byte[8192]; - while (dis.read(readBuffer, 0, readBuffer.length) != -1) { - // not using - } - } catch (IOException e) { - Slog.w(TAG, "Could not read manifest"); - return null; - } finally { - IoUtils.closeQuietly(dis); - } - - final byte[] digest = md.digest(); - return new ManifestDigest(digest); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ManifestDigest)) { - return false; - } - - final ManifestDigest other = (ManifestDigest) o; - - return this == other || Arrays.equals(mDigest, other.mDigest); - } - - @Override - public int hashCode() { - return Arrays.hashCode(mDigest); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX.length() - + (mDigest.length * 3) + 1); - - sb.append(TO_STRING_PREFIX); - - final int N = mDigest.length; - for (int i = 0; i < N; i++) { - final byte b = mDigest[i]; - HexDump.appendByteAsHex(sb, b, false); - sb.append(','); - } - sb.append('}'); - - return sb.toString(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByteArray(mDigest); - } - - public static final Parcelable.Creator<ManifestDigest> CREATOR - = new Parcelable.Creator<ManifestDigest>() { - public ManifestDigest createFromParcel(Parcel source) { - return new ManifestDigest(source); - } - - public ManifestDigest[] newArray(int size) { - return new ManifestDigest[size]; - } - }; - -} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 69f508e54119..bccc3d95c4ea 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -93,6 +93,109 @@ public abstract class PackageManager { } /** + * As a guiding principle: + * <p> + * {@code GET_} flags are used to request additional data that may have been + * elided to save wire space. + * <p> + * {@code MATCH_} flags are used to include components or packages that + * would have otherwise been omitted from a result set by current system + * state. + */ + + /** @hide */ + @IntDef(flag = true, value = { + GET_ACTIVITIES, + GET_RECEIVERS, + GET_SERVICES, + GET_PROVIDERS, + GET_INSTRUMENTATION, + GET_INTENT_FILTERS, + GET_SIGNATURES, + GET_META_DATA, + GET_GIDS, + GET_SHARED_LIBRARY_FILES, + GET_URI_PERMISSION_PATTERNS, + GET_PERMISSIONS, + GET_CONFIGURATIONS, + MATCH_UNINSTALLED_PACKAGES, + MATCH_DISABLED_COMPONENTS, + MATCH_DISABLED_UNTIL_USED_COMPONENTS, + MATCH_DEBUG_TRIAGED_MISSING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PackageInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + GET_SHARED_LIBRARY_FILES, + MATCH_UNINSTALLED_PACKAGES, + MATCH_SYSTEM_ONLY, + MATCH_DEBUG_TRIAGED_MISSING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApplicationInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + GET_SHARED_LIBRARY_FILES, + MATCH_UNINSTALLED_PACKAGES, + MATCH_DISABLED_COMPONENTS, + MATCH_DISABLED_UNTIL_USED_COMPONENTS, + MATCH_ALL, + MATCH_DEFAULT_ONLY, + MATCH_ENCRYPTION_AWARE, + MATCH_ENCRYPTION_AWARE_AND_UNAWARE, + MATCH_ENCRYPTION_UNAWARE, + MATCH_SYSTEM_ONLY, + MATCH_DEBUG_TRIAGED_MISSING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ComponentInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + GET_SHARED_LIBRARY_FILES, + GET_RESOLVED_FILTER, + MATCH_UNINSTALLED_PACKAGES, + MATCH_DISABLED_COMPONENTS, + MATCH_DISABLED_UNTIL_USED_COMPONENTS, + MATCH_ALL, + MATCH_DEFAULT_ONLY, + MATCH_ENCRYPTION_AWARE, + MATCH_ENCRYPTION_AWARE_AND_UNAWARE, + MATCH_ENCRYPTION_UNAWARE, + MATCH_SYSTEM_ONLY, + MATCH_DEBUG_TRIAGED_MISSING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ResolveInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionGroupInfoFlags {} + + /** @hide */ + @IntDef(flag = true, value = { + GET_META_DATA, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface InstrumentationInfoFlags {} + + /** * {@link PackageInfo} flag: return information about * activities in the package in {@link PackageInfo#activities}. */ @@ -161,9 +264,15 @@ public abstract class PackageManager { public static final int GET_GIDS = 0x00000100; /** + * @deprecated replaced with {@link #MATCH_DISABLED_COMPONENTS} + */ + @Deprecated + public static final int GET_DISABLED_COMPONENTS = 0x00000200; + + /** * {@link PackageInfo} flag: include disabled components in the returned info. */ - public static final int GET_DISABLED_COMPONENTS = 0x00000200; + public static final int MATCH_DISABLED_COMPONENTS = 0x00000200; /** * {@link ApplicationInfo} flag: return the @@ -190,6 +299,12 @@ public abstract class PackageManager { public static final int GET_PERMISSIONS = 0x00001000; /** + * @deprecated replaced with {@link #MATCH_UNINSTALLED_PACKAGES} + */ + @Deprecated + public static final int GET_UNINSTALLED_PACKAGES = 0x00002000; + + /** * Flag parameter to retrieve some information about all applications (even * uninstalled ones) which have data directories. This state could have * resulted if applications have been deleted with flag @@ -199,7 +314,7 @@ public abstract class PackageManager { * Note: this flag may cause less information about currently installed * applications to be returned. */ - public static final int GET_UNINSTALLED_PACKAGES = 0x00002000; + public static final int MATCH_UNINSTALLED_PACKAGES = 0x00002000; /** * {@link PackageInfo} flag: return information about @@ -211,12 +326,18 @@ public abstract class PackageManager { public static final int GET_CONFIGURATIONS = 0x00004000; /** + * @deprecated replaced with {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS}. + */ + @Deprecated + public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000; + + /** * {@link PackageInfo} flag: include disabled components which are in * that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} * in the returned info. Note that if you set this flag, applications * that are in this disabled state will be reported as enabled. */ - public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000; + public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000; /** * Resolution and querying flag: if set, only filters that support the @@ -227,48 +348,55 @@ public abstract class PackageManager { public static final int MATCH_DEFAULT_ONLY = 0x00010000; /** - * Querying flag: if set and if the platform is doing any filtering of the results, then - * the filtering will not happen. This is a synonym for saying that all results should - * be returned. + * Querying flag: if set and if the platform is doing any filtering of the + * results, then the filtering will not happen. This is a synonym for saying + * that all results should be returned. + * <p> + * <em>This flag should be used with extreme care.</em> */ public static final int MATCH_ALL = 0x00020000; /** - * {@link PackageInfo} flag: include only components which are encryption - * unaware in the returned info, regardless of the current user state. + * Querying flag: include only components which are encryption unaware in + * the returned info, regardless of the current user state. */ - public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 0x00040000; + public static final int MATCH_ENCRYPTION_UNAWARE = 0x00040000; /** - * {@link PackageInfo} flag: include only components which are encryption - * aware in the returned info, regardless of the current user state. + * Querying flag: include only components which are encryption aware in the + * returned info, regardless of the current user state. */ - public static final int MATCH_ENCRYPTION_AWARE_ONLY = 0x00080000; + public static final int MATCH_ENCRYPTION_AWARE = 0x00080000; /** - * {@link PackageInfo} flag: include both encryption aware and unaware - * components in the returned info, regardless of the current user state. + * Querying flag: include both encryption aware and unaware components in + * the returned info, regardless of the current user state. */ - public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE_ONLY - | MATCH_ENCRYPTION_UNAWARE_ONLY; + public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE + | MATCH_ENCRYPTION_UNAWARE; /** - * {@link PackageInfo} flag: include only components from applications that - * are marked with {@link ApplicationInfo#FLAG_SYSTEM}. - * - * @hide + * Querying flag: include only components from applications that are marked + * with {@link ApplicationInfo#FLAG_SYSTEM}. */ public static final int MATCH_SYSTEM_ONLY = 0x00100000; /** - * {@link PackageInfo} flag: use the default encryption matching behavior - * based on user state. Internal flag used to indicate that a system - * component has done their homework and verified their encryption-aware - * behavior. + * Internal flag used to indicate that a system component has done their + * homework and verified that they correctly handle packages and components + * that come and go over time. In particular: + * <ul> + * <li>Apps installed on external storage, which will appear to be + * uninstalled while the the device is ejected. + * <li>Apps with encryption unaware components, which will appear to not + * exist while the device is locked. + * </ul> * + * @see #MATCH_UNINSTALLED_PACKAGES + * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE * @hide */ - public static final int MATCH_ENCRYPTION_DEFAULT = 0x10000000; + public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000; /** * Flag for {@link addCrossProfileIntentFilter}: if this flag is set: @@ -2131,7 +2259,7 @@ public abstract class PackageManager { * @see #GET_SIGNATURES * @see #GET_UNINSTALLED_PACKAGES */ - public abstract PackageInfo getPackageInfo(String packageName, int flags) + public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) throws NameNotFoundException; /** @@ -2171,8 +2299,8 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) - public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) - throws NameNotFoundException; + public abstract PackageInfo getPackageInfoAsUser(String packageName, + @PackageInfoFlags int flags, int userId) throws NameNotFoundException; /** * Map from the current package names in use on the device to whatever @@ -2270,7 +2398,7 @@ public abstract class PackageManager { * @return Returns a {@link PermissionInfo} containing information about the * permission. */ - public abstract PermissionInfo getPermissionInfo(String name, int flags) + public abstract PermissionInfo getPermissionInfo(String name, @PermissionInfoFlags int flags) throws NameNotFoundException; /** @@ -2289,7 +2417,7 @@ public abstract class PackageManager { * about all of the permissions in the given group. */ public abstract List<PermissionInfo> queryPermissionsByGroup(String group, - int flags) throws NameNotFoundException; + @PermissionInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular group of @@ -2307,7 +2435,7 @@ public abstract class PackageManager { * about the permission. */ public abstract PermissionGroupInfo getPermissionGroupInfo(String name, - int flags) throws NameNotFoundException; + @PermissionGroupInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the known permission groups in the system. @@ -2318,7 +2446,8 @@ public abstract class PackageManager { * @return Returns a list of {@link PermissionGroupInfo} containing * information about all of the known permission groups. */ - public abstract List<PermissionGroupInfo> getAllPermissionGroups(int flags); + public abstract List<PermissionGroupInfo> getAllPermissionGroups( + @PermissionGroupInfoFlags int flags); /** * Retrieve all of the information we know about a particular @@ -2348,7 +2477,7 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public abstract ApplicationInfo getApplicationInfo(String packageName, - int flags) throws NameNotFoundException; + @ApplicationInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular activity @@ -2371,7 +2500,7 @@ public abstract class PackageManager { * @see #GET_SHARED_LIBRARY_FILES */ public abstract ActivityInfo getActivityInfo(ComponentName component, - int flags) throws NameNotFoundException; + @ComponentInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular receiver @@ -2394,7 +2523,7 @@ public abstract class PackageManager { * @see #GET_SHARED_LIBRARY_FILES */ public abstract ActivityInfo getReceiverInfo(ComponentName component, - int flags) throws NameNotFoundException; + @ComponentInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular service @@ -2416,7 +2545,7 @@ public abstract class PackageManager { * @see #GET_SHARED_LIBRARY_FILES */ public abstract ServiceInfo getServiceInfo(ComponentName component, - int flags) throws NameNotFoundException; + @ComponentInfoFlags int flags) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular content @@ -2438,7 +2567,7 @@ public abstract class PackageManager { * @see #GET_SHARED_LIBRARY_FILES */ public abstract ProviderInfo getProviderInfo(ComponentName component, - int flags) throws NameNotFoundException; + @ComponentInfoFlags int flags) throws NameNotFoundException; /** * Return a List of all packages that are installed @@ -2474,7 +2603,7 @@ public abstract class PackageManager { * @see #GET_SIGNATURES * @see #GET_UNINSTALLED_PACKAGES */ - public abstract List<PackageInfo> getInstalledPackages(int flags); + public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags); /** * Return a List of all installed packages that are currently @@ -2507,7 +2636,7 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public abstract List<PackageInfo> getPackagesHoldingPermissions( - String[] permissions, int flags); + String[] permissions, @PackageInfoFlags int flags); /** * Return a List of all packages that are installed on the device, for a specific user. @@ -2546,7 +2675,7 @@ public abstract class PackageManager { * * @hide */ - public abstract List<PackageInfo> getInstalledPackages(int flags, int userId); + public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags, int userId); /** * Check whether a particular package has been granted a particular @@ -2886,7 +3015,7 @@ public abstract class PackageManager { * @see #GET_SHARED_LIBRARY_FILES * @see #GET_UNINSTALLED_PACKAGES */ - public abstract List<ApplicationInfo> getInstalledApplications(int flags); + public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags); /** * Gets the ephemeral applications the user recently used. Requires @@ -3021,7 +3150,7 @@ public abstract class PackageManager { * @see #GET_INTENT_FILTERS * @see #GET_RESOLVED_FILTER */ - public abstract ResolveInfo resolveActivity(Intent intent, int flags); + public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags); /** * Determine the best action to perform for a given Intent for a given user. This @@ -3054,7 +3183,8 @@ public abstract class PackageManager { * * @hide */ - public abstract ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId); + public abstract ResolveInfo resolveActivityAsUser(Intent intent, @ResolveInfoFlags int flags, + int userId); /** * Retrieve all activities that can be performed for the given intent. @@ -3077,7 +3207,7 @@ public abstract class PackageManager { * @see #GET_RESOLVED_FILTER */ public abstract List<ResolveInfo> queryIntentActivities(Intent intent, - int flags); + @ResolveInfoFlags int flags); /** * Retrieve all activities that can be performed for the given intent, for a specific user. @@ -3101,8 +3231,7 @@ public abstract class PackageManager { * @hide */ public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, - int flags, int userId); - + @ResolveInfoFlags int flags, int userId); /** * Retrieve a set of activities that should be presented to the user as @@ -3134,7 +3263,7 @@ public abstract class PackageManager { * @see #GET_RESOLVED_FILTER */ public abstract List<ResolveInfo> queryIntentActivityOptions( - ComponentName caller, Intent[] specifics, Intent intent, int flags); + ComponentName caller, Intent[] specifics, Intent intent, @ResolveInfoFlags int flags); /** * Retrieve all receivers that can handle a broadcast of the given intent. @@ -3151,7 +3280,7 @@ public abstract class PackageManager { * @see #GET_RESOLVED_FILTER */ public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent, - int flags); + @ResolveInfoFlags int flags); /** * Retrieve all receivers that can handle a broadcast of the given intent, for a specific @@ -3171,7 +3300,7 @@ public abstract class PackageManager { * @hide */ public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent, - int flags, int userId); + @ResolveInfoFlags int flags, int userId); /** * Determine the best service to handle for a given Intent. @@ -3187,7 +3316,7 @@ public abstract class PackageManager { * @see #GET_INTENT_FILTERS * @see #GET_RESOLVED_FILTER */ - public abstract ResolveInfo resolveService(Intent intent, int flags); + public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags); /** * Retrieve all services that can match the given intent. @@ -3205,7 +3334,7 @@ public abstract class PackageManager { * @see #GET_RESOLVED_FILTER */ public abstract List<ResolveInfo> queryIntentServices(Intent intent, - int flags); + @ResolveInfoFlags int flags); /** * Retrieve all services that can match the given intent for a given user. @@ -3226,11 +3355,11 @@ public abstract class PackageManager { * @hide */ public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent, - int flags, int userId); + @ResolveInfoFlags int flags, int userId); /** {@hide} */ public abstract List<ResolveInfo> queryIntentContentProvidersAsUser( - Intent intent, int flags, int userId); + Intent intent, @ResolveInfoFlags int flags, int userId); /** * Retrieve all providers that can match the given intent. @@ -3244,7 +3373,8 @@ public abstract class PackageManager { * @see #GET_INTENT_FILTERS * @see #GET_RESOLVED_FILTER */ - public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags); + public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, + @ResolveInfoFlags int flags); /** * Find a single content provider by its base path name. @@ -3256,7 +3386,7 @@ public abstract class PackageManager { * else null. */ public abstract ProviderInfo resolveContentProvider(String name, - int flags); + @ComponentInfoFlags int flags); /** * Find a single content provider by its base path name. @@ -3269,7 +3399,8 @@ public abstract class PackageManager { * else null. * @hide */ - public abstract ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId); + public abstract ProviderInfo resolveContentProviderAsUser(String name, + @ComponentInfoFlags int flags, int userId); /** * Retrieve content provider information. @@ -3290,7 +3421,7 @@ public abstract class PackageManager { * <em>If there are no matching providers, null is returned.</em> */ public abstract List<ProviderInfo> queryContentProviders( - String processName, int uid, int flags); + String processName, int uid, @ComponentInfoFlags int flags); /** * Retrieve all of the information we know about a particular @@ -3307,8 +3438,8 @@ public abstract class PackageManager { * @return InstrumentationInfo containing information about the * instrumentation. */ - public abstract InstrumentationInfo getInstrumentationInfo( - ComponentName className, int flags) throws NameNotFoundException; + public abstract InstrumentationInfo getInstrumentationInfo(ComponentName className, + @InstrumentationInfoFlags int flags) throws NameNotFoundException; /** * Retrieve information about available instrumentation code. May be used @@ -3324,8 +3455,8 @@ public abstract class PackageManager { * matching available Instrumentation. Returns an empty list if * there is no instrumentation available for the given package. */ - public abstract List<InstrumentationInfo> queryInstrumentation( - String targetPackage, int flags); + public abstract List<InstrumentationInfo> queryInstrumentation(String targetPackage, + @InstrumentationInfoFlags int flags); /** * Retrieve an image from a package. This is a low-level API used by @@ -3761,14 +3892,13 @@ public abstract class PackageManager { * @see #GET_SIGNATURES * */ - public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) { + public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) { final PackageParser parser = new PackageParser(); final File apkFile = new File(archiveFilePath); try { PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0); if ((flags & GET_SIGNATURES) != 0) { parser.collectCertificates(pkg, 0); - parser.collectManifestDigest(pkg); } PackageUserState state = new PackageUserState(); return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); @@ -3828,14 +3958,12 @@ public abstract class PackageManager { * @param verificationURI The location of the supplementary verification * file. This can be a 'file:' or a 'content:' URI. May be * {@code null}. - * @param manifestDigest an object that holds the digest of the package - * which can be used to verify ownership. May be {@code null}. * @param encryptionParams if the package to be installed is encrypted, * these parameters describing the encryption and authentication * used. May be {@code null}. * @hide * @deprecated Use {@link #installPackageWithVerification(Uri, - * PackageInstallObserver, int, String, Uri, ManifestDigest, + * PackageInstallObserver, int, String, Uri, * ContainerEncryptionParams)} instead. This method will * continue to be supported but the older observer interface * will not get additional failure details. @@ -3843,7 +3971,7 @@ public abstract class PackageManager { // @SystemApi public abstract void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, - Uri verificationURI, ManifestDigest manifestDigest, + Uri verificationURI, ContainerEncryptionParams encryptionParams); /** @@ -3952,8 +4080,6 @@ public abstract class PackageManager { * @param verificationURI The location of the supplementary verification * file. This can be a 'file:' or a 'content:' URI. May be * {@code null}. - * @param manifestDigest an object that holds the digest of the package - * which can be used to verify ownership. May be {@code null}. * @param encryptionParams if the package to be installed is encrypted, * these parameters describing the encryption and authentication * used. May be {@code null}. @@ -3961,7 +4087,7 @@ public abstract class PackageManager { */ public abstract void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, - Uri verificationURI, ManifestDigest manifestDigest, + Uri verificationURI, ContainerEncryptionParams encryptionParams); /** @@ -4423,7 +4549,7 @@ public abstract class PackageManager { * @see #GET_SERVICES * @see #GET_SIGNATURES */ - public abstract List<PackageInfo> getPreferredPackages(int flags); + public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags); /** * @deprecated This is a protected API that should not have been available diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f445cf8208b6..236cf64ae8ce 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1032,31 +1032,6 @@ public class PackageParser { } /** - * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the - * APK. If it successfully scanned the package and found the - * {@code AndroidManifest.xml}, {@code true} is returned. - */ - public void collectManifestDigest(Package pkg) throws PackageParserException { - pkg.manifestDigest = null; - - // TODO: extend to gather digest for split APKs - try { - final StrictJarFile jarFile = new StrictJarFile(pkg.baseCodePath); - try { - final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - if (je != null) { - pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); - } - } finally { - jarFile.close(); - } - } catch (IOException | RuntimeException e) { - throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, - "Failed to collect manifest digest"); - } - } - - /** * Collect certificates from all the APKs described in the given package, * populating {@link Package#mSignatures}. Also asserts that all APK * contents are signed correctly and consistently. @@ -3223,12 +3198,19 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING; } - a.info.resizeable = sa.getBoolean( - R.styleable.AndroidManifestActivity_resizeableActivity, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N); + if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) { + a.info.flags |= ActivityInfo.FLAG_RESIZEABLE; + + if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, + false)) { + a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; + } + } - a.info.supportsPip = a.info.resizeable ? sa.getBoolean( - R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false; + if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) { + a.info.flags |= ActivityInfo.FLAG_ALWAYS_FOCUSABLE; + } a.info.screenOrientation = sa.getInt( R.styleable.AndroidManifestActivity_screenOrientation, @@ -4492,12 +4474,6 @@ public class PackageParser { /* The required account type without which this application will not function */ public String mRequiredAccountType; - /** - * Digest suitable for comparing whether this package's manifest is the - * same as another. - */ - public ManifestDigest manifestDigest; - public String mOverlayTarget; public int mOverlayPriority; public boolean mTrustedOverlay; diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java index cfb447334406..945858e6d3c6 100644 --- a/core/java/android/content/pm/ParceledListSlice.java +++ b/core/java/android/content/pm/ParceledListSlice.java @@ -24,6 +24,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -50,6 +51,10 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable { private final List<T> mList; + public static <T extends Parcelable> ParceledListSlice<T> emptyList() { + return new ParceledListSlice<T>(Collections.<T> emptyList()); + } + public ParceledListSlice(List<T> list) { mList = list; } diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java index e5119b621ee5..f90d295b8478 100644 --- a/core/java/android/content/pm/VerificationParams.java +++ b/core/java/android/content/pm/VerificationParams.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.pm.ManifestDigest; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -51,12 +50,6 @@ public class VerificationParams implements Parcelable { private int mInstallerUid; /** - * An object that holds the digest of the package which can be used to - * verify ownership. - */ - private final ManifestDigest mManifestDigest; - - /** * Creates verification specifications for installing with application verification. * * @param verificationURI The location of the supplementary verification @@ -67,16 +60,13 @@ public class VerificationParams implements Parcelable { * May be {@code null}. * @param originatingUid UID of the application that the install request originated * from, or NO_UID if not present - * @param manifestDigest an object that holds the digest of the package - * which can be used to verify ownership. May be {@code null}. */ public VerificationParams(Uri verificationURI, Uri originatingURI, Uri referrer, - int originatingUid, ManifestDigest manifestDigest) { + int originatingUid) { mVerificationURI = verificationURI; mOriginatingURI = originatingURI; mReferrer = referrer; mOriginatingUid = originatingUid; - mManifestDigest = manifestDigest; mInstallerUid = NO_UID; } @@ -97,10 +87,6 @@ public class VerificationParams implements Parcelable { return mOriginatingUid; } - public ManifestDigest getManifestDigest() { - return mManifestDigest; - } - /** @return NO_UID when not set */ public int getInstallerUid() { return mInstallerUid; @@ -155,14 +141,6 @@ public class VerificationParams implements Parcelable { return false; } - if (mManifestDigest == null) { - if (other.mManifestDigest != null) { - return false; - } - } else if (!mManifestDigest.equals(other.mManifestDigest)) { - return false; - } - if (mInstallerUid != other.mInstallerUid) { return false; } @@ -178,8 +156,7 @@ public class VerificationParams implements Parcelable { hash += 7 * (mOriginatingURI == null ? 1 : mOriginatingURI.hashCode()); hash += 11 * (mReferrer == null ? 1 : mReferrer.hashCode()); hash += 13 * mOriginatingUid; - hash += 17 * (mManifestDigest == null ? 1 : mManifestDigest.hashCode()); - hash += 19 * mInstallerUid; + hash += 17 * mInstallerUid; return hash; } @@ -196,8 +173,6 @@ public class VerificationParams implements Parcelable { sb.append(mReferrer.toString()); sb.append(",mOriginatingUid="); sb.append(mOriginatingUid); - sb.append(",mManifestDigest="); - sb.append(mManifestDigest.toString()); sb.append(",mInstallerUid="); sb.append(mInstallerUid); sb.append('}'); @@ -211,7 +186,6 @@ public class VerificationParams implements Parcelable { dest.writeParcelable(mOriginatingURI, 0); dest.writeParcelable(mReferrer, 0); dest.writeInt(mOriginatingUid); - dest.writeParcelable(mManifestDigest, 0); dest.writeInt(mInstallerUid); } @@ -221,7 +195,6 @@ public class VerificationParams implements Parcelable { mOriginatingURI = source.readParcelable(Uri.class.getClassLoader()); mReferrer = source.readParcelable(Uri.class.getClassLoader()); mOriginatingUid = source.readInt(); - mManifestDigest = source.readParcelable(ManifestDigest.class.getClassLoader()); mInstallerUid = source.readInt(); } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 7669053cc803..ee6aec2910aa 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -698,6 +698,18 @@ public final class AssetManager implements AutoCloseable { */ public native final String[] getLocales(); + /** + * Same as getLocales(), except that locales that are only provided by the system (i.e. those + * present in framework-res.apk or its overlays) will not be listed. + * + * For example, if the "system" assets support English, French, and German, and the additional + * assets support Cherokee and French, getLocales() would return + * [Cherokee, English, French, German], while getNonSystemLocales() would return + * [Cherokee, French]. + * {@hide} + */ + public native final String[] getNonSystemLocales(); + /** {@hide} */ public native final Configuration[] getSizeConfigurations(); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 60c6e825e1be..b01633e641f3 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1976,7 +1976,21 @@ public class Resources { if (setLocalesToDefault || mResolvedLocale == null || (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) { - mResolvedLocale = locales.getFirstMatch(mAssets.getLocales()); + if (locales.size() == 1) { + // This is an optimization to avoid the JNI call(s) when the result of + // getFirstMatchWithEnglishSupported() does not depend on the supported locales. + mResolvedLocale = locales.getPrimary(); + } else { + String[] supportedLocales = mAssets.getNonSystemLocales(); + if (LocaleList.isPseudoLocalesOnly(supportedLocales)) { + // We fallback to all locales (including system locales) if there was no + // locale specifically supported by the assets. This is to properly support + // apps that only rely on the shared system assets and don't need assets of + // their own. + supportedLocales = mAssets.getLocales(); + } + mResolvedLocale = locales.getFirstMatchWithEnglishSupported(supportedLocales); + } } mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, adjustLanguageTag(mResolvedLocale.toLanguageTag()), diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b883f9c736d0..4eaee0b44fc5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6374,6 +6374,13 @@ public final class Settings { public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES = "force_resizable_activities"; + /** + * Whether to enable experimental freeform support for windows. + * @hide + */ + public static final String DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT + = "enable_freeform_support"; + /** * Whether user has enabled development settings. */ diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java index 46aa1af247de..37ec72589570 100644 --- a/core/java/android/security/NetworkSecurityPolicy.java +++ b/core/java/android/security/NetworkSecurityPolicy.java @@ -68,7 +68,6 @@ public class NetworkSecurityPolicy { * TLS or STARTTLS) is permitted for communicating with {@code hostname} for this process. * * @see #isCleartextTrafficPermitted() - * @hide */ public boolean isCleartextTrafficPermitted(String hostname) { return libcore.net.NetworkSecurityPolicy.getInstance() diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java index 71d9d5d0e04d..4de36cd2c0a9 100644 --- a/core/java/android/security/net/config/ApplicationConfig.java +++ b/core/java/android/security/net/config/ApplicationConfig.java @@ -120,6 +120,32 @@ public final class ApplicationConfig { return mTrustManager; } + /** + * Returns {@code true} if cleartext traffic is permitted for this application, which is the + * case only if all configurations permit cleartext traffic. For finer-grained policy use + * {@link #isCleartextTrafficPermitted(String)}. + */ + public boolean isCleartextTrafficPermitted() { + ensureInitialized(); + if (mConfigs != null) { + for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) { + if (!entry.second.isCleartextTrafficPermitted()) { + return false; + } + } + } + + return mDefaultConfig.isCleartextTrafficPermitted(); + } + + /** + * Returns {@code true} if cleartext traffic is permitted for this application when connecting + * to {@code hostname}. + */ + public boolean isCleartextTrafficPermitted(String hostname) { + return getConfigForHostname(hostname).isCleartextTrafficPermitted(); + } + private void ensureInitialized() { synchronized(mLock) { if (mInitialized) { diff --git a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java new file mode 100644 index 000000000000..e7d17c27c2c9 --- /dev/null +++ b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.net.config; + +/** + * {@link libcore.net.NetworkSecurityPolicy} based on an {@link ApplicationConfig}. + * + * @hide + */ +public class ConfigNetworkSecurityPolicy extends libcore.net.NetworkSecurityPolicy { + private final ApplicationConfig mConfig; + + public ConfigNetworkSecurityPolicy(ApplicationConfig config) { + mConfig = config; + } + + @Override + public boolean isCleartextTrafficPermitted() { + return mConfig.isCleartextTrafficPermitted(); + } + + @Override + public boolean isCleartextTrafficPermitted(String hostname) { + return mConfig.isCleartextTrafficPermitted(hostname); + } +} diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java index 5ebc7ac5f242..0f6687341395 100644 --- a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java +++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java @@ -40,5 +40,6 @@ public final class NetworkSecurityConfigProvider extends Provider { throw new RuntimeException("Failed to install provider as highest priority provider." + " Provider was installed at position " + pos); } + libcore.net.NetworkSecurityPolicy.setInstance(new ConfigNetworkSecurityPolicy(config)); } } diff --git a/core/java/android/service/notification/INotificationAssistant.aidl b/core/java/android/service/notification/INotificationAssistant.aidl deleted file mode 100644 index 5c5f358b99b9..000000000000 --- a/core/java/android/service/notification/INotificationAssistant.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2015, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.notification; - -import android.service.notification.NotificationAdjustment; -import android.service.notification.IStatusBarNotificationHolder; -import android.service.notification.NotificationRankingUpdate; - -/** @hide */ -interface INotificationAssistant -{ - void onListenerConnected(in NotificationRankingUpdate update); - void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder, - in NotificationRankingUpdate update); - void onNotificationRankingUpdate(in NotificationRankingUpdate update); - void onListenerHintsChanged(int hints); - void onInterruptionFilterChanged(int interruptionFilter); - NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user); - void onNotificationVisibilityChanged(String key, long time, boolean visible); - void onNotificationClick(String key, long time); - void onNotificationActionClick(String key, long time, int actionIndex); - void onNotificationRemoved(String key, long time, int reason); -}
\ No newline at end of file diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index e6bf6ba97492..a0de17f4b7cf 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -23,6 +23,7 @@ import android.service.notification.NotificationRankingUpdate; /** @hide */ oneway interface INotificationListener { + // listeners and assistants void onListenerConnected(in NotificationRankingUpdate update); void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder, in NotificationRankingUpdate update); @@ -31,4 +32,11 @@ oneway interface INotificationListener void onNotificationRankingUpdate(in NotificationRankingUpdate update); void onListenerHintsChanged(int hints); void onInterruptionFilterChanged(int interruptionFilter); + + // assistants only + void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user); + void onNotificationVisibilityChanged(String key, long time, boolean visible); + void onNotificationClick(String key, long time); + void onNotificationActionClick(String key, long time, int actionIndex); + void onNotificationRemovedReason(String key, long time, int reason); } diff --git a/core/java/android/service/notification/NotificationAdjustment.java b/core/java/android/service/notification/NotificationAdjustment.java deleted file mode 100644 index c5f0db9bf5f1..000000000000 --- a/core/java/android/service/notification/NotificationAdjustment.java +++ /dev/null @@ -1,57 +0,0 @@ -package android.service.notification; - -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -public class NotificationAdjustment implements Parcelable { - int mImportance; - CharSequence mExplanation; - Uri mReference; - - /** - * Create a notification importance adjustment. - * - * @param importance The final importance of the notification. - * @param explanation A human-readable justification for the adjustment. - * @param reference A reference to an external object that augments the - * explanation, such as a - * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, - * or null. - */ - public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) { - mImportance = importance; - mExplanation = explanation; - mReference = reference; - } - - private NotificationAdjustment(Parcel source) { - this(source.readInt(), source.readCharSequence(), - (Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader())); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mImportance); - dest.writeCharSequence(mExplanation); - dest.writeParcelable(mReference, 0); - } - - public static final Parcelable.Creator<NotificationAdjustment> CREATOR - = new Parcelable.Creator<NotificationAdjustment>() { - @Override - public NotificationAdjustment createFromParcel(Parcel source) { - return new NotificationAdjustment(source); - } - - @Override - public NotificationAdjustment[] newArray(int size) { - return new NotificationAdjustment[size]; - } - }; -} diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 7ce5e1f9da9c..3a8956ec15d9 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -20,8 +20,11 @@ import android.annotation.SdkConstant; import android.app.Notification; import android.content.Intent; import android.net.Uri; +import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; /** * A service that helps the user manage notifications by modifying the @@ -39,6 +42,8 @@ import android.os.Parcelable; * </service></pre> */ public abstract class NotificationAssistantService extends NotificationListenerService { + private static final String TAG = "NotificationAssistant"; + /** * The {@link Intent} that must be declared as handled by the service. */ @@ -85,6 +90,36 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** Notification was canceled because it was an invisible member of a group. */ public static final int REASON_GROUP_OPTIMIZATION = 13; + public class Adjustment { + int mImportance; + CharSequence mExplanation; + Uri mReference; + + /** + * Create a notification importance adjustment. + * + * @param importance The final importance of the notification. + * @param explanation A human-readable justification for the adjustment. + * @param reference A reference to an external object that augments the + * explanation, such as a + * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, + * or null. + */ + public Adjustment(int importance, CharSequence explanation, Uri reference) { + mImportance = importance; + mExplanation = explanation; + mReference = reference; + } + } + + @Override + public IBinder onBind(Intent intent) { + if (mWrapper == null) { + mWrapper = new NotificationAssistantWrapper(); + } + return mWrapper; + } + /** * A notification was posted by an app. Called before alert. * @@ -93,7 +128,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS * @param user true if the initial importance reflects an explicit user preference. * @return an adjustment or null to take no action, within 100ms. */ - abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn, + abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance, boolean user); /** @@ -150,9 +185,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS * @param key the notification key * @param adjustment the new importance with an explanation */ - public final void adjustImportance(String key, NotificationAdjustment adjustment) + public final void adjustImportance(String key, Adjustment adjustment) { - // TODO: pack up the adjustment and send it to the NotificationManager. + if (!isBound()) return; + try { + getNotificationInterface().setImportanceFromAssistant(mWrapper, key, + adjustment.mImportance, adjustment.mExplanation); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } } /** @@ -160,7 +201,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS * be fired when the host notification is deleted, or when this annotation * is removed or replaced. * - * @param key the notification key + * @param key the key of the notification to be annotated * @param annotation the new annotation object */ public final void setAnnotation(String key, Notification annotation) @@ -171,10 +212,74 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * Remove the annotation from a notification. * - * @param key the notification key + * @param key the key of the notification to be cleansed of annotatons */ public final void clearAnnotation(String key) { // TODO: ask the NotificationManager to clear the annotation. } + + private class NotificationAssistantWrapper extends NotificationListenerWrapper { + @Override + public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, + int importance, boolean user) throws RemoteException { + StatusBarNotification sbn; + try { + sbn = sbnHolder.get(); + } catch (RemoteException e) { + Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); + return; + } + + try { + Adjustment adjustment = + NotificationAssistantService.this.onNotificationEnqueued(sbn, importance, user); + if (adjustment != null) { + adjustImportance(sbn.getKey(), adjustment); + } + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationEnqueued", t); + } + } + + @Override + public void onNotificationVisibilityChanged(String key, long time, boolean visible) + throws RemoteException { + try { + NotificationAssistantService.this.onNotificationVisibilityChanged(key, time, + visible); + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationVisibilityChanged", t); + } + } + + @Override + public void onNotificationClick(String key, long time) throws RemoteException { + try { + NotificationAssistantService.this.onNotificationClick(key, time); + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationClick", t); + } + } + + @Override + public void onNotificationActionClick(String key, long time, int actionIndex) + throws RemoteException { + try { + NotificationAssistantService.this.onNotificationActionClick(key, time, actionIndex); + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationActionClick", t); + } + } + + @Override + public void onNotificationRemovedReason(String key, long time, int reason) + throws RemoteException { + try { + NotificationAssistantService.this.onNotificationRemoved(key, time, reason); + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationRemoved", t); + } + } + } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 1e62edc6634f..b42d9eaf9cb5 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -15,6 +15,7 @@ */ package android.service.notification; +import android.service.notification.IStatusBarNotificationHolder; import android.annotation.SystemApi; import android.annotation.SdkConstant; @@ -122,6 +123,8 @@ public abstract class NotificationListenerService extends Service { NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; public static final int SUPPRESSED_EFFECT_PEEK = NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; + public static final int SUPPRESSED_EFFECT_SCREEN_ON = + NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; /** * The full trim of the StatusBarNotification including all its features. @@ -151,7 +154,8 @@ public abstract class NotificationListenerService extends Service { @SystemApi public static final int TRIM_LIGHT = 1; - private INotificationListenerWrapper mWrapper = null; + /** @hide */ + protected NotificationListenerWrapper mWrapper = null; private RankingMap mRankingMap; private INotificationManager mNoMan; @@ -291,7 +295,8 @@ public abstract class NotificationListenerService extends Service { // optional } - private final INotificationManager getNotificationInterface() { + /** @hide */ + protected final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); @@ -634,12 +639,13 @@ public abstract class NotificationListenerService extends Service { @Override public IBinder onBind(Intent intent) { if (mWrapper == null) { - mWrapper = new INotificationListenerWrapper(); + mWrapper = new NotificationListenerWrapper(); } return mWrapper; } - private boolean isBound() { + /** @hide */ + protected boolean isBound() { if (mWrapper == null) { Log.w(TAG, "Notification listener service not yet bound."); return false; @@ -664,7 +670,7 @@ public abstract class NotificationListenerService extends Service { int currentUser) throws RemoteException { mSystemContext = context; if (mWrapper == null) { - mWrapper = new INotificationListenerWrapper(); + mWrapper = new NotificationListenerWrapper(); } INotificationManager noMan = getNotificationInterface(); noMan.registerListener(mWrapper, componentName, currentUser); @@ -716,7 +722,8 @@ public abstract class NotificationListenerService extends Service { } } - private class INotificationListenerWrapper extends INotificationListener.Stub { + /** @hide */ + protected class NotificationListenerWrapper extends INotificationListener.Stub { @Override public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, NotificationRankingUpdate update) { @@ -817,6 +824,35 @@ public abstract class NotificationListenerService extends Service { Log.w(TAG, "Error running onInterruptionFilterChanged", t); } } + + @Override + public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder, + int importance, boolean user) throws RemoteException { + // no-op in the listener + } + + @Override + public void onNotificationVisibilityChanged(String key, long time, boolean visible) + throws RemoteException { + // no-op in the listener + } + + @Override + public void onNotificationClick(String key, long time) throws RemoteException { + // no-op in the listener + } + + @Override + public void onNotificationActionClick(String key, long time, int actionIndex) + throws RemoteException { + // no-op in the listener + } + + @Override + public void onNotificationRemovedReason(String key, long time, int reason) + throws RemoteException { + // no-op in the listener + } } private void applyUpdate(NotificationRankingUpdate update) { @@ -927,7 +963,8 @@ public abstract class NotificationListenerService extends Service { /** * Returns the type(s) of visual effects that should be suppressed for this notification. - * See {@link #SUPPRESSED_EFFECT_LIGHTS}, {@link #SUPPRESSED_EFFECT_PEEK}}. + * See {@link #SUPPRESSED_EFFECT_LIGHTS}, {@link #SUPPRESSED_EFFECT_PEEK}, + * {@link #SUPPRESSED_EFFECT_SCREEN_ON}. */ public int getSuppressedVisualEffects() { return mSuppressedVisualEffects; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 541623d736f1..468884347fc5 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -79,6 +79,7 @@ public class ZenModeConfig implements Parcelable { private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false; private static final boolean DEFAULT_ALLOW_PEEK = true; private static final boolean DEFAULT_ALLOW_LIGHTS = true; + private static final boolean DEFAULT_ALLOW_SCREEN_ON = true; private static final int XML_VERSION = 2; private static final String ZEN_TAG = "zen"; @@ -95,6 +96,7 @@ public class ZenModeConfig implements Parcelable { private static final String ALLOW_ATT_EVENTS = "events"; private static final String ALLOW_ATT_PEEK = "peek"; private static final String ALLOW_ATT_LIGHTS = "lights"; + private static final String ALLOW_ATT_SCREEN_ON = "screen_on"; private static final String CONDITION_TAG = "condition"; private static final String CONDITION_ATT_COMPONENT = "component"; @@ -128,6 +130,7 @@ public class ZenModeConfig implements Parcelable { public int user = UserHandle.USER_SYSTEM; public boolean allowPeek = DEFAULT_ALLOW_PEEK; public boolean allowLights = DEFAULT_ALLOW_LIGHTS; + public boolean allowScreenOn = DEFAULT_ALLOW_SCREEN_ON; public ZenRule manualRule; public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); @@ -156,6 +159,7 @@ public class ZenModeConfig implements Parcelable { } allowPeek = source.readInt() == 1; allowLights = source.readInt() == 1; + allowScreenOn = source.readInt() == 1; } @Override @@ -185,6 +189,7 @@ public class ZenModeConfig implements Parcelable { } dest.writeInt(allowPeek ? 1 : 0); dest.writeInt(allowLights ? 1 : 0); + dest.writeInt(allowScreenOn ? 1 : 0); } @Override @@ -200,6 +205,7 @@ public class ZenModeConfig implements Parcelable { .append(",allowEvents=").append(allowEvents) .append(",allowPeek=").append(allowPeek) .append(",allowLights=").append(allowLights) + .append(",allowScreenOn=").append(allowScreenOn) .append(",automaticRules=").append(automaticRules) .append(",manualRule=").append(manualRule) .append(']').toString(); @@ -240,6 +246,9 @@ public class ZenModeConfig implements Parcelable { if (allowLights != to.allowLights) { d.addLine("allowLights", allowLights, to.allowLights); } + if (allowScreenOn != to.allowScreenOn) { + d.addLine("allowScreenOn", allowScreenOn, to.allowScreenOn); + } final ArraySet<String> allRules = new ArraySet<>(); addKeys(allRules, automaticRules); addKeys(allRules, to.automaticRules); @@ -339,6 +348,7 @@ public class ZenModeConfig implements Parcelable { && other.allowEvents == allowEvents && other.allowPeek == allowPeek && other.allowLights == allowLights + && other.allowScreenOn == allowScreenOn && other.user == user && Objects.equals(other.automaticRules, automaticRules) && Objects.equals(other.manualRule, manualRule); @@ -348,7 +358,7 @@ public class ZenModeConfig implements Parcelable { public int hashCode() { return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, allowPeek, allowLights, - user, automaticRules, manualRule); + allowScreenOn, user, automaticRules, manualRule); } private static String toDayList(int[] days) { @@ -435,6 +445,8 @@ public class ZenModeConfig implements Parcelable { } rt.allowPeek = safeBoolean(parser, ALLOW_ATT_PEEK, DEFAULT_ALLOW_PEEK); rt.allowLights = safeBoolean(parser, ALLOW_ATT_LIGHTS, DEFAULT_ALLOW_LIGHTS); + rt.allowScreenOn = + safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); } else if (AUTOMATIC_TAG.equals(tag)) { @@ -465,6 +477,7 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom)); out.attribute(null, ALLOW_ATT_PEEK, Boolean.toString(allowPeek)); out.attribute(null, ALLOW_ATT_LIGHTS, Boolean.toString(allowLights)); + out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowScreenOn)); out.endTag(null, ALLOW_TAG); if (manualRule != null) { @@ -643,6 +656,9 @@ public class ZenModeConfig implements Parcelable { if (!allowLights) { suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; } + if (!allowScreenOn) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; + } priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders, @@ -681,6 +697,8 @@ public class ZenModeConfig implements Parcelable { if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { allowPeek = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_PEEK) == 0; allowLights = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_LIGHTS) == 0; + allowScreenOn = + (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0; } } diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl index 7e70501de39b..75da82fc43c0 100644 --- a/core/java/android/service/quicksettings/IQSService.aidl +++ b/core/java/android/service/quicksettings/IQSService.aidl @@ -24,4 +24,5 @@ import android.service.quicksettings.Tile; interface IQSService { void updateQsTile(in Tile tile); void onShowDialog(in Tile tile); + void setTileMode(in ComponentName component, int mode); } diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl index 63a4c5e30df9..4997f754d2a7 100644 --- a/core/java/android/service/quicksettings/IQSTileService.aidl +++ b/core/java/android/service/quicksettings/IQSTileService.aidl @@ -22,6 +22,7 @@ import android.service.quicksettings.IQSService; * @hide */ oneway interface IQSTileService { + void setQSService(in IQSService service); void setQSTile(in Tile tile); void onTileAdded(); void onTileRemoved(); diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java index a53fc59d362c..6104913642f8 100644 --- a/core/java/android/service/quicksettings/Tile.java +++ b/core/java/android/service/quicksettings/Tile.java @@ -37,11 +37,12 @@ public final class Tile implements Parcelable { private static final String TAG = "Tile"; private ComponentName mComponentName; - private IQSService mService; private Icon mIcon; private CharSequence mLabel; private CharSequence mContentDescription; + private IQSService mService; + /** * @hide */ @@ -52,8 +53,14 @@ public final class Tile implements Parcelable { /** * @hide */ - public Tile(ComponentName componentName, IQSService service) { + public Tile(ComponentName componentName) { mComponentName = componentName; + } + + /** + * @hide + */ + public void setService(IQSService service) { mService = service; } @@ -65,6 +72,13 @@ public final class Tile implements Parcelable { } /** + * @hide + */ + public IQSService getQsService() { + return mService; + } + + /** * Gets the current icon for the tile. */ public Icon getIcon() { @@ -137,21 +151,8 @@ public final class Tile implements Parcelable { } } - /** - * @hide - * Notifies the IQSService that this tile is showing a dialog. - */ - void onShowDialog() { - try { - mService.onShowDialog(this); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't onShowDialog"); - } - } - @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongInterface(mService); if (mComponentName != null) { dest.writeByte((byte) 1); mComponentName.writeToParcel(dest, flags); @@ -169,7 +170,6 @@ public final class Tile implements Parcelable { } private void readFromParcel(Parcel source) { - mService = IQSService.Stub.asInterface(source.readStrongBinder()); if (source.readByte() != 0) { mComponentName = ComponentName.CREATOR.createFromParcel(source); } else { diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index d8787b4caca7..1e134c7efaba 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -15,8 +15,11 @@ */ package android.service.quicksettings; +import android.Manifest; import android.app.Dialog; import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; @@ -26,29 +29,29 @@ import android.os.RemoteException; import android.view.WindowManager; /** - * A QSTileService provides the user a tile that can be added to Quick Settings. + * A TileService provides the user a tile that can be added to Quick Settings. * Quick Settings is a space provided that allows the user to change settings and * take quick actions without leaving the context of their current app. * - * <p>The lifecycle of a QSTileService is different from some other services in + * <p>The lifecycle of a TileService is different from some other services in * that it may be unbound during parts of its lifecycle. Any of the following * lifecycle events can happen indepently in a separate binding/creation of the * service.</p> * * <ul> - * <li>When a tile is added by the user its QSTileService will be bound to and + * <li>When a tile is added by the user its TileService will be bound to and * {@link #onTileAdded()} will be called.</li> * * <li>When a tile should be up to date and listing will be indicated by * {@link #onStartListening()} and {@link #onStopListening()}.</li> * - * <li>When the user removes a tile from Quick Settings {@link #onStopListening()} + * <li>When the user removes a tile from Quick Settings {@link #onTileRemoved()} * will be called.</li> * </ul> - * <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE} + * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE} * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE". * The label and icon for the service will be used as the default label and - * icon for the tile. Here is an example QSTileService declaration.</p> + * icon for the tile. Here is an example TileService declaration.</p> * <pre class="prettyprint"> * {@literal * <service @@ -67,15 +70,52 @@ import android.view.WindowManager; public class TileService extends Service { /** - * Action that identifies a Service as being a QSTileService. + * Action that identifies a Service as being a TileService. */ public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + /** + * The tile mode hasn't been set yet. + * @hide + */ + public static final int TILE_MODE_UNSET = 0; + + /** + * Constant to be returned by {@link #onTileAdded}. + * <p> + * Passive mode is the default mode for tiles. The System will tell the tile + * when it is most important to update by putting it in the listening state. + */ + public static final int TILE_MODE_PASSIVE = 1; + + /** + * Constant to be returned by {@link #onTileAdded}. + * <p> + * Active mode is for tiles which already listen and keep track of their state in their + * own process. These tiles may request to send an update to the System while their process + * is alive using {@link #requestListeningState}. The System will only bind these tiles + * on its own when a click needs to occur. + */ + public static final int TILE_MODE_ACTIVE = 2; + + /** + * Used to notify SysUI that Listening has be requested. + * @hide + */ + public static final String ACTION_REQUEST_LISTENING + = "android.service.quicksettings.action.REQUEST_LISTENING"; + + /** + * @hide + */ + public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT"; + private final H mHandler = new H(Looper.getMainLooper()); private boolean mListening = false; private Tile mTile; private IBinder mToken; + private IQSService mService; @Override public void onDestroy() { @@ -92,8 +132,12 @@ public class TileService extends Service { * Note that this is not guaranteed to be called between {@link #onCreate()} * and {@link #onStartListening()}, it will only be called when the tile is added * and not on subsequent binds. + * + * @see #TILE_MODE_PASSIVE + * @see #TILE_MODE_ACTIVE */ - public void onTileAdded() { + public int onTileAdded() { + return TILE_MODE_PASSIVE; } /** @@ -138,7 +182,10 @@ public class TileService extends Service { dialog.getWindow().getAttributes().token = mToken; dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG); dialog.show(); - getQsTile().onShowDialog(); + try { + mService.onShowDialog(mTile); + } catch (RemoteException e) { + } } /** @@ -156,6 +203,11 @@ public class TileService extends Service { public IBinder onBind(Intent intent) { return new IQSTileService.Stub() { @Override + public void setQSService(IQSService service) throws RemoteException { + mHandler.obtainMessage(H.MSG_SET_SERVICE, service).sendToTarget(); + } + + @Override public void setQSTile(Tile tile) throws RemoteException { mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget(); } @@ -194,6 +246,7 @@ public class TileService extends Service { private static final int MSG_TILE_ADDED = 4; private static final int MSG_TILE_REMOVED = 5; private static final int MSG_TILE_CLICKED = 6; + private static final int MSG_SET_SERVICE = 7; public H(Looper looper) { super(looper); @@ -202,11 +255,28 @@ public class TileService extends Service { @Override public void handleMessage(Message msg) { switch (msg.what) { + case MSG_SET_SERVICE: + mService = (IQSService) msg.obj; + if (mTile != null) { + mTile.setService(mService); + } + break; case MSG_SET_TILE: mTile = (Tile) msg.obj; + if (mService != null && mTile != null) { + mTile.setService(mService); + } break; case MSG_TILE_ADDED: - TileService.this.onTileAdded(); + int mode = TileService.this.onTileAdded(); + if (mService == null) { + return; + } + try { + mService.setTileMode(new ComponentName(TileService.this, + TileService.this.getClass()), mode); + } catch (RemoteException e) { + } break; case MSG_TILE_REMOVED: TileService.this.onTileRemoved(); @@ -230,4 +300,16 @@ public class TileService extends Service { } } } + + /** + * Requests that a tile be put in the listening state so it can send an update. + * + * This method is only applicable to tiles that return {@link #TILE_MODE_ACTIVE} from + * {@link #onTileAdded()}, and will do nothing otherwise. + */ + public static final void requestListeningState(Context context, ComponentName component) { + Intent intent = new Intent(ACTION_REQUEST_LISTENING); + intent.putExtra(EXTRA_COMPONENT, component); + context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE); + } } diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java index 1becfb4a0e23..f22cde0c1ca7 100644 --- a/core/java/android/util/LocaleList.java +++ b/core/java/android/util/LocaleList.java @@ -28,12 +28,6 @@ import com.android.internal.annotations.GuardedBy; import java.util.HashSet; import java.util.Locale; -// TODO: We don't except too many LocaleLists to exist at the same time, and -// we need access to the data at native level, so we should pass the data -// down to the native level, create a map of every list seen there, take a -// pointer back, and just keep that pointer in the Java-level object, so -// things could be copied very quickly. - /** * LocaleList is an immutable list of Locales, typically used to keep an * ordered user preferences for locales. @@ -219,6 +213,20 @@ public final class LocaleList implements Parcelable { } } + private static final String STRING_EN_XA = "en-XA"; + private static final String STRING_AR_XB = "ar-XB"; + private static final Locale LOCALE_EN_XA = new Locale("en", "XA"); + private static final Locale LOCALE_AR_XB = new Locale("ar", "XB"); + private static final int NUM_PSEUDO_LOCALES = 2; + + private static boolean isPseudoLocale(String locale) { + return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale); + } + + private static boolean isPseudoLocale(Locale locale) { + return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale); + } + private static int matchScore(Locale supported, Locale desired) { if (supported.equals(desired)) { return 1; // return early so we don't do unnecessary computation @@ -226,6 +234,11 @@ public final class LocaleList implements Parcelable { if (!supported.getLanguage().equals(desired.getLanguage())) { return 0; } + if (isPseudoLocale(supported) || isPseudoLocale(desired)) { + // The locales are not the same, but the languages are the same, and one of the locales + // is a pseudo-locale. So this is not a match. + return 0; + } // There is no match if the two locales use different scripts. This will most imporantly // take care of traditional vs simplified Chinese. final String supportedScr = getLikelyScript(supported); @@ -233,24 +246,26 @@ public final class LocaleList implements Parcelable { return supportedScr.equals(desiredScr) ? 1 : 0; } - /** - * Returns the first match in the locale list given an unordered array of supported locales - * in BCP47 format. - * - * If the locale list is empty, null would be returned. - */ - @Nullable - public Locale getFirstMatch(String[] supportedLocales) { + private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn"); + + private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) { if (mList.length == 1) { // just one locale, perhaps the most common scenario return mList[0]; } if (mList.length == 0) { // empty locale list return null; } - // TODO: Figure out what to if en-XA or ar-XB are in the locale list int bestIndex = Integer.MAX_VALUE; - for (String tag : supportedLocales) { - final Locale supportedLocale = Locale.forLanguageTag(tag); + final int numSupportedLocales = + supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0); + for (int i = 0; i < numSupportedLocales; i++) { + final Locale supportedLocale; + if (assumeEnglishIsSupported) { + // Try English first, so we can return early if it's in the LocaleList + supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]); + } else { + supportedLocale = Locale.forLanguageTag(supportedLocales[i]); + } // We expect the average length of locale lists used for locale resolution to be // smaller than three, so it's OK to do this as an O(mn) algorithm. for (int idx = 0; idx < mList.length; idx++) { @@ -271,6 +286,47 @@ public final class LocaleList implements Parcelable { } } + /** + * Returns the first match in the locale list given an unordered array of supported locales + * in BCP47 format. + * + * If the locale list is empty, null would be returned. + */ + @Nullable + public Locale getFirstMatch(String[] supportedLocales) { + return computeFirstMatch(supportedLocales, false /* assume English is not supported */); + } + + /** + * Same as getFirstMatch(), but with English assumed to be supported, even if it's not. + * {@hide} + */ + @Nullable + public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) { + return computeFirstMatch(supportedLocales, true /* assume English is supported */); + } + + /** + * Returns true if the array of locale tags only contains empty locales and pseudolocales. + * Assumes that there is no repetition in the input. + * {@hide} + */ + public static boolean isPseudoLocalesOnly(String[] supportedLocales) { + if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) { + // This is for optimization. Since there's no repetition in the input, if we have more + // than the number of pseudo-locales plus one for the empty string, it's guaranteed + // that we have some meaninful locale in the list, so the list is not "practically + // empty". + return false; + } + for (String locale : supportedLocales) { + if (!locale.isEmpty() && !isPseudoLocale(locale)) { + return false; + } + } + return true; + } + private final static Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/view/IDockDividerVisibilityListener.aidl b/core/java/android/view/IDockedStackListener.aidl index a7d5cda9b44f..77fa7e25f9df 100644 --- a/core/java/android/view/IDockDividerVisibilityListener.aidl +++ b/core/java/android/view/IDockedStackListener.aidl @@ -22,6 +22,15 @@ package android.view; * * @hide */ -oneway interface IDockDividerVisibilityListener { - void onDockDividerVisibilityChanged(boolean visible); +oneway interface IDockedStackListener { + + /** + * Will fire when an app is shown in side by side mode and a divider should be shown. + */ + void onDividerVisibilityChanged(boolean visible); + + /** + * Called when the docked stack gets created or removed. + */ + void onDockedStackExistsChanged(boolean exists); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 7a379d50ba05..84d312d59b64 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -29,7 +29,7 @@ import android.os.Bundle; import android.os.IRemoteCallback; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.IDockDividerVisibilityListener; +import android.view.IDockedStackListener; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; @@ -101,11 +101,14 @@ interface IWindowManager * the task doesn't exist yet. * @param configuration Configuration that is being used with this task. * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds. + * @param alwaysFocusable True if the app windows are always focusable regardless of the stack + * they are in. */ void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, - in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack); + in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack, + boolean alwaysFocusable); /** * * @param token The token we are adding to the input task Id. @@ -354,7 +357,17 @@ interface IWindowManager void setDockedStackResizing(boolean resizing); /** - * Registers a listener that will be called when the dock divider changes its visibility. + * Registers a listener that will be called when the dock divider changes its visibility or when + * the docked stack gets added/removed. */ - void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener); + void registerDockedStackListener(IDockedStackListener listener); + + /** + * Updates the dim layer used while resizing. + * + * @param visible Whether the dim layer should be visible. + * @param targetStackId The id of the task stack the dim layer should be placed on. + * @param alpha The translucency of the dim layer, between 0 and 1. + */ + void setResizeDimLayer(boolean visible, int targetStackId, float alpha); } diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index aa29636398e4..914bd5684b4a 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -555,6 +555,26 @@ public abstract class LayoutInflater { } } + private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader(); + + private final boolean verifyClassLoader(Constructor<? extends View> constructor) { + final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader(); + if (constructorLoader == BOOT_CLASS_LOADER) { + // fast path for boot class loader (most common case?) - always ok + return true; + } + // in all normal cases (no dynamic code loading), we will exit the following loop on the + // first iteration (i.e. when the declaring classloader is the contexts class loader). + ClassLoader cl = mContext.getClassLoader(); + do { + if (constructorLoader == cl) { + return true; + } + cl = cl.getParent(); + } while (cl != null); + return false; + } + /** * Low-level function for instantiating a view by name. This attempts to * instantiate a view class of the given <var>name</var> found in this @@ -575,6 +595,10 @@ public abstract class LayoutInflater { public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor<? extends View> constructor = sConstructorMap.get(name); + if (constructor != null && !verifyClassLoader(constructor)) { + constructor = null; + sConstructorMap.remove(name); + } Class<? extends View> clazz = null; try { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index cd93dab0c48b..1c243929fa49 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1419,8 +1419,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager case DragEvent.ACTION_DRAG_ENDED: { // Release the bookkeeping now that the drag lifecycle has ended - if (mChildrenInterestedInDrag != null) { - for (View child : mChildrenInterestedInDrag) { + final HashSet<View> childrenInterestedInDrag = mChildrenInterestedInDrag; + if (childrenInterestedInDrag != null) { + for (View child : childrenInterestedInDrag) { // If a child was interested in the ongoing drag, it's told that it's over if (child.dispatchDragEvent(event)) { retval = true; @@ -1428,12 +1429,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.mPrivateFlags2 &= ~View.DRAG_MASK; child.refreshDrawableState(); } - - mChildrenInterestedInDrag.clear(); - if (mCurrentDragStartEvent != null) { - mCurrentDragStartEvent.recycle(); - mCurrentDragStartEvent = null; - } + childrenInterestedInDrag.clear(); + } + if (mCurrentDragStartEvent != null) { + mCurrentDragStartEvent.recycle(); + mCurrentDragStartEvent = null; } if (mIsInterestedInDrag) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f2b4fb3a042f..1c9f3b403ef8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3315,6 +3315,7 @@ public final class ViewRootImpl implements ViewParent, && mPendingStableInsets.equals(args.arg6) && mPendingVisibleInsets.equals(args.arg3) && mPendingOutsets.equals(args.arg7) + && mPendingBackDropFrame.equals(args.arg8) && args.arg4 == null) { break; } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 19e290b71a97..7e981939034b 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1467,10 +1467,14 @@ public class PopupWindow { } if (mClipToScreen) { + final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0]; + final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1]; + p.x += winOffsetX; + p.y += winOffsetY; final int displayFrameWidth = displayFrame.right - displayFrame.left; final int right = p.x + p.width; - if (right > displayFrameWidth) { - p.x -= right - displayFrameWidth; + if (right > displayFrame.right) { + p.x -= right - displayFrame.right; } if (p.x < displayFrame.left) { @@ -1479,10 +1483,9 @@ public class PopupWindow { } if (mOverlapAnchor) { - final int displayFrameHeight = displayFrame.bottom - displayFrame.top; final int bottom = p.y + p.height; if (bottom > displayFrame.bottom) { - p.y -= bottom - displayFrameHeight; + p.y -= bottom - displayFrame.bottom; } } else { if (onTop) { @@ -1494,6 +1497,8 @@ public class PopupWindow { p.y = Math.max(p.y, displayFrame.top); } } + p.x -= winOffsetX; + p.y -= winOffsetY; } p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL; diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index 4b6e7e4a03d2..575ef0293361 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -483,6 +483,12 @@ public class ToolbarActionBar extends ActionBar { return true; } + @Override + public void onDestroy() { + // Remove any invalidation callbacks + mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator); + } + public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { mMenuVisibilityListeners.add(listener); } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index bfc56dbed61a..4b821abec6fb 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -36,6 +36,8 @@ public class MetricsLogger implements MetricsConstants { public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263; public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 264; public static final int QS_COLOR_MATRIX = 265; + public static final int QS_CUSTOM = 266; + public static final int ACTION_ZEN_ALLOW_SCREEN_ON = 267; /** * Logged when the user docks a window from recents by longpressing a task and dragging it to diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 4a0f3fc23032..d8ec22a0f697 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -587,10 +587,6 @@ static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, return doDecode(env, stream.release(), NULL, options); } -static void nativeRequestCancel(JNIEnv*, jobject joptions) { - // Deprecated -} - static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE; @@ -630,10 +626,6 @@ static const JNINativeMethod gMethods[] = { }, }; -static const JNINativeMethod gOptionsMethods[] = { - { "requestCancel", "()V", (void*)nativeRequestCancel } -}; - int register_android_graphics_BitmapFactory(JNIEnv* env) { jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options"); gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap", @@ -665,8 +657,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) { gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V"); - android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory$Options", - gOptionsMethods, NELEM(gOptionsMethods)); return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory", gMethods, NELEM(gMethods)); } diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index ab0df555ccfc..7c8dbe8ecf23 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -36,12 +36,12 @@ namespace android { static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) { - FontLanguage fontLanguage; - if (lang != NULL) { - ScopedUtfChars str(env, lang); - fontLanguage = FontLanguage(str.c_str(), str.size()); + if (lang == NULL) { + return (jlong)new FontFamily(variant); } - return (jlong)new FontFamily(fontLanguage, variant); + ScopedUtfChars str(env, lang); + uint32_t langId = FontStyle::registerLanguageList(str.c_str()); + return (jlong)new FontFamily(langId, variant); } static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) { diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 654d148e2029..0a25a0a8ae38 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -313,94 +313,10 @@ namespace PaintGlue { obj->setTextAlign(align); } - // generate bcp47 identifier for the supplied locale - static void toLanguageTag(char* output, size_t outSize, - const char* locale) { - if (output == NULL || outSize <= 0) { - return; - } - if (locale == NULL) { - output[0] = '\0'; - return; - } - char canonicalChars[ULOC_FULLNAME_CAPACITY]; - UErrorCode uErr = U_ZERO_ERROR; - uloc_canonicalize(locale, canonicalChars, ULOC_FULLNAME_CAPACITY, - &uErr); - if (U_SUCCESS(uErr)) { - char likelyChars[ULOC_FULLNAME_CAPACITY]; - uErr = U_ZERO_ERROR; - uloc_addLikelySubtags(canonicalChars, likelyChars, - ULOC_FULLNAME_CAPACITY, &uErr); - if (U_SUCCESS(uErr)) { - uErr = U_ZERO_ERROR; - uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr); - if (U_SUCCESS(uErr)) { - return; - } else { - ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars, - u_errorName(uErr)); - } - } else { - ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s", - canonicalChars, u_errorName(uErr)); - } - } else { - ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale, - u_errorName(uErr)); - } - // unable to build a proper language identifier - output[0] = '\0'; - } - - static void toLanguageTags(std::string* output, const char* locales) { - if (output == NULL) { - return; - } - if (locales == NULL) { - output->clear(); - return; - } - - char langTag[ULOC_FULLNAME_CAPACITY]; - const char* commaLoc = strchr(locales, ','); - if (commaLoc == NULL) { - assert(locales[0] != '\0'); // the string should not be empty - toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locales); - *output = langTag; - return; - } - - size_t len = strlen(locales); - char locale[len]; - output->clear(); - output->reserve(len); - const char* lastStart = locales; - do { - assert(lastStart > commaLoc); // the substring should not be empty - strncpy(locale, lastStart, commaLoc - lastStart); - locale[commaLoc - lastStart] = '\0'; - toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale); - if (langTag[0] != '\0') { - output->append(langTag); - output->push_back(','); - } - lastStart = commaLoc + 1; - commaLoc = strchr(lastStart, ','); - } while (commaLoc != NULL); - assert(lastStart[0] != '\0'); // the final substring should not be empty - toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, lastStart); - if (langTag[0] != '\0') { - output->append(langTag); - } - } - static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) { Paint* obj = reinterpret_cast<Paint*>(objHandle); ScopedUtfChars localesChars(env, locales); - std::string buf; - toLanguageTags(&buf, localesChars.c_str()); - jint minikinLangListId = FontStyle::registerLanguageList(buf); + jint minikinLangListId = FontStyle::registerLanguageList(localesChars.c_str()); obj->setMinikinLangListId(minikinLangListId); return minikinLangListId; } diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 90606a35b5a2..3473d9dab732 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -578,7 +578,7 @@ static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject cla return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) { Vector<String8> locales; @@ -587,7 +587,7 @@ static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject return NULL; } - am->getLocales(&locales); + am->getLocales(&locales, includeSystemLocales); const int N = locales.size(); @@ -608,6 +608,16 @@ static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject return result; } +static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, true /* include system locales */); +} + +static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) +{ + return getLocales(env, clazz, false /* don't include system locales */); +} + static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { jobject result = env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); @@ -2154,6 +2164,8 @@ static const JNINativeMethod gAssetManagerMethods[] = { // Resources. { "getLocales", "()[Ljava/lang/String;", (void*) android_content_AssetManager_getLocales }, + { "getNonSystemLocales", "()[Ljava/lang/String;", + (void*) android_content_AssetManager_getNonSystemLocales }, { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", (void*) android_content_AssetManager_getSizeConfigurations }, { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1090f90fbbef..c154e91bb559 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -107,6 +107,7 @@ <protected-broadcast android:name="android.backup.intent.CLEAR" /> <protected-broadcast android:name="android.backup.intent.INIT" /> + <protected-broadcast android:name="android.bluetooth.intent.DISCOVERABLE_TIMEOUT" /> <protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" /> @@ -888,6 +889,11 @@ android:protectionLevel="normal" android:permissionFlags="hidden"/> + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.FLASHLIGHT" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + <!-- ====================================================================== --> <!-- INSTALL PERMISSIONS --> <!-- ====================================================================== --> @@ -1193,14 +1199,6 @@ android:description="@string/permdesc_vibrate" android:protectionLevel="normal" /> - <!-- Allows access to the flashlight. - <p>Protection level: normal - --> - <permission android:name="android.permission.FLASHLIGHT" - android:label="@string/permlab_flashlight" - android:description="@string/permdesc_flashlight" - android:protectionLevel="normal" /> - <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. <p>Protection level: normal @@ -1901,7 +1899,7 @@ <!-- Allows an application to bind to third party quick settings tiles. <p>Should only be requested by the System, should be required by - QSTileService declarations.--> + TileService declarations.--> <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" android:protectionLevel="signature" /> diff --git a/core/res/res/values-land/config.xml b/core/res/res/values-land/config.xml new file mode 100644 index 000000000000..7308dc5882c1 --- /dev/null +++ b/core/res/res/values-land/config.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<resources> + <integer name="config_dockedStackDividerSnapMode">2</integer> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml index 5a007ce97483..7f57ded0521e 100644 --- a/core/res/res/values-sw600dp/config.xml +++ b/core/res/res/values-sw600dp/config.xml @@ -40,5 +40,6 @@ <!-- Use a larger scaling span for larger screen devices. --> <dimen name="config_minScalingSpan">32mm</dimen> + <integer name="config_dockedStackDividerSnapMode">1</integer> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 2a1108155f17..58a77e809235 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1857,6 +1857,11 @@ <attr name="lockTaskMode" /> <attr name="showForAllUsers" /> <attr name="encryptionAware" /> + <!-- @hide This activity is always focusable regardless of if it is in a task/stack whose + activities are normally not focusable. + For example, {@link android.R.attr#supportsPictureInPicture} activities are placed + in a task/stack that isn't focusable. This flag allows them to be focusable.--> + <attr name="alwaysFocusable" format="boolean" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c03d4711120e..d13a6227c04f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2410,4 +2410,11 @@ <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. --> <string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string> + + <!-- Controls the snap mode for the docked stack divider + 0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio + 1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) + 2 - 1 snap target: 1:1 + --> + <integer name="config_dockedStackDividerSnapMode">0</integer> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index ef6b467ed103..37b2c1288d91 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -429,4 +429,6 @@ <item type="dimen" format="integer" name="time_picker_column_start_material">0</item> <item type="dimen" format="integer" name="time_picker_column_end_material">1</item> + + <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item> </resources> diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml index eee577178c38..3cfd9f4be0ce 100644 --- a/core/res/res/values/locale_config.xml +++ b/core/res/res/values/locale_config.xml @@ -49,6 +49,7 @@ <item>ar-SY</item> <!-- Arabic (Syria) --> <item>ar-TD</item> <!-- Arabic (Chad) --> <item>ar-TN</item> <!-- Arabic (Tunisia) --> + <item>ar-XB</item> <!-- Right-to-left pseudolocale --> <item>ar-YE</item> <!-- Arabic (Yemen) --> <item>as-IN</item> <!-- Assamese (India) --> <item>asa-TZ</item> <!-- Asu (Tanzania) --> @@ -195,6 +196,7 @@ <item>en-VI</item> <!-- English (U.S. Virgin Islands) --> <item>en-VU</item> <!-- English (Vanuatu) --> <item>en-WS</item> <!-- English (Samoa) --> + <item>en-XA</item> <!-- Left-to-right pseudolocale --> <item>en-ZA</item> <!-- English (South Africa) --> <item>en-ZM</item> <!-- English (Zambia) --> <item>en-ZW</item> <!-- English (Zimbabwe) --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 347f2f912e41..be9ba62baa3a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1026,11 +1026,6 @@ <string name="permdesc_vibrate">Allows the app to control the vibrator.</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_flashlight">control flashlight</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_flashlight">Allows the app to control the flashlight.</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_callPhone">directly call phone numbers</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_callPhone">Allows the app to call phone numbers diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3c1d944a5272..845c8c935ab9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1492,6 +1492,8 @@ <java-symbol type="bool" name="target_honeycomb_needs_options_menu" /> <java-symbol type="dimen" name="docked_stack_divider_thickness" /> <java-symbol type="dimen" name="docked_stack_divider_insets" /> + <java-symbol type="integer" name="config_dockedStackDividerSnapMode" /> + <java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" /> <java-symbol type="dimen" name="navigation_bar_height" /> <java-symbol type="dimen" name="navigation_bar_height_landscape" /> <java-symbol type="dimen" name="navigation_bar_width" /> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 7cd25afe5bcd..ee8921ef8228 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, EnabledTestApp/src) LOCAL_DX_FLAGS := --core-library -LOCAL_AAPT_FLAGS = -0 dat -0 gld +LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa LOCAL_STATIC_JAVA_LIBRARIES := \ core-tests-support \ android-common \ diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 51778361291d..c0453f8ac991 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -146,6 +146,16 @@ </intent-filter> </activity> + <activity android:name="android.widget.DatePickerActivity" + android:label="DatePickerActivity" + android:screenOrientation="portrait" + android:theme="@android:style/Theme.Material.Light"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + <activity android:name="android.widget.focus.DescendantFocusability" android:label="DescendantFocusability"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/core/tests/coretests/res/layout/datepicker_layout.xml b/core/tests/coretests/res/layout/datepicker_layout.xml new file mode 100644 index 000000000000..a79f87d97aa9 --- /dev/null +++ b/core/tests/coretests/res/layout/datepicker_layout.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <DatePicker + android:id="@+id/datePicker" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true"/> + <EditText + android:id="@+id/belowPicker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Some Text" + android:textAlignment="center" + android:layout_below="@id/datePicker"/> +</RelativeLayout> diff --git a/core/tests/coretests/res/values-fa/strings.xml b/core/tests/coretests/res/values-fa/strings.xml new file mode 100644 index 000000000000..c83f5f1bebf4 --- /dev/null +++ b/core/tests/coretests/res/values-fa/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="dummy_string">رشتهٔ الکی</string> +</resources> diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml index 04b0478c96d1..ef915bba1013 100644 --- a/core/tests/coretests/res/values/strings.xml +++ b/core/tests/coretests/res/values/strings.xml @@ -139,4 +139,7 @@ <!-- RestrictionsManagerTest --> <string name="restrictionManager_title">Title</string> <string name="restrictionManager_desc">Description</string> + + <!-- ResourcesLocaleResolutionTest --> + <string name="dummy_string">dummy string</string> </resources> diff --git a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java deleted file mode 100644 index 37495e135725..000000000000 --- a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm; - -import android.os.Parcel; -import android.test.AndroidTestCase; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; - -public class ManifestDigestTest extends AndroidTestCase { - private static final byte[] MESSAGE_1 = { - (byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF - }; - - public void testManifestDigest_FromInputStream_Null() { - assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null", - ManifestDigest.fromInputStream(null)); - } - - public void testManifestDigest_FromInputStream_ThrowsIoException() { - InputStream is = new InputStream() { - @Override - public int read() throws IOException { - throw new IOException(); - } - }; - - assertNull("InputStream threw exception, so ManifestDigest should be null", - ManifestDigest.fromInputStream(is)); - } - - public void testManifestDigest_Equals() throws Exception { - InputStream is = new ByteArrayInputStream(MESSAGE_1); - - ManifestDigest expected = - new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1)); - - ManifestDigest actual = ManifestDigest.fromInputStream(is); - assertEquals(expected, actual); - - ManifestDigest unexpected = new ManifestDigest(new byte[0]); - assertFalse(unexpected.equals(actual)); - } - - public void testManifestDigest_Parcel() throws Exception { - InputStream is = new ByteArrayInputStream(MESSAGE_1); - - ManifestDigest digest = ManifestDigest.fromInputStream(is); - - Parcel p = Parcel.obtain(); - digest.writeToParcel(p, 0); - p.setDataPosition(0); - - ManifestDigest fromParcel = ManifestDigest.CREATOR.createFromParcel(p); - - assertEquals("ManifestDigest going through parceling should be the same as before: " - + digest.toString() + " and " + fromParcel.toString(), digest, - fromParcel); - } -} diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java index 9b216cba0bd5..d963812db622 100644 --- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java +++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.pm.ManifestDigest; import android.content.pm.VerificationParams; import android.net.Uri; import android.os.Parcel; @@ -33,7 +32,6 @@ public class VerificationParamsTest extends AndroidTestCase { private final static String VERIFICATION_URI_STRING = "http://verification.uri/path"; private final static String ORIGINATING_URI_STRING = "http://originating.uri/path"; private final static String REFERRER_STRING = "http://referrer.uri/path"; - private final static byte[] DIGEST_BYTES = "fake digest".getBytes(); private final static int INSTALLER_UID = 42; private final static Uri VERIFICATION_URI = Uri.parse(VERIFICATION_URI_STRING); @@ -42,11 +40,9 @@ public class VerificationParamsTest extends AndroidTestCase { private final static int ORIGINATING_UID = 10042; - private final static ManifestDigest MANIFEST_DIGEST = new ManifestDigest(DIGEST_BYTES); - public void testParcel() throws Exception { VerificationParams expected = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); Parcel parcel = Parcel.obtain(); expected.writeToParcel(parcel, 0); @@ -61,85 +57,70 @@ public class VerificationParamsTest extends AndroidTestCase { assertEquals(REFERRER, actual.getReferrer()); assertEquals(ORIGINATING_UID, actual.getOriginatingUid()); - - assertEquals(MANIFEST_DIGEST, actual.getManifestDigest()); } public void testEquals_Success() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertEquals(params1, params2); } public void testEquals_VerificationUri_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse("http://a.different.uri/"), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertFalse(params1.equals(params2)); } public void testEquals_OriginatingUri_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertFalse(params1.equals(params2)); } public void testEquals_Referrer_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse("http://a.different.uri/"), ORIGINATING_UID, - new ManifestDigest(DIGEST_BYTES)); + Uri.parse("http://a.different.uri/"), ORIGINATING_UID); assertFalse(params1.equals(params2)); } public void testEquals_Originating_Uid_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); - - VerificationParams params2 = new VerificationParams( - Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), 12345, new ManifestDigest(DIGEST_BYTES)); - - assertFalse(params1.equals(params2)); - } - - public void testEquals_ManifestDigest_Failure() throws Exception { - VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, - new ManifestDigest("a different digest".getBytes())); + Uri.parse(REFERRER_STRING), 12345); assertFalse(params1.equals(params2)); } public void testEquals_InstallerUid_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); params2.setInstallerUid(INSTALLER_UID); assertFalse(params1.equals(params2)); @@ -147,78 +128,65 @@ public class VerificationParamsTest extends AndroidTestCase { public void testHashCode_Success() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertEquals(params1.hashCode(), params2.hashCode()); } public void testHashCode_VerificationUri_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams(null, Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertFalse(params1.hashCode() == params2.hashCode()); } public void testHashCode_OriginatingUri_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); assertFalse(params1.hashCode() == params2.hashCode()); } public void testHashCode_Referrer_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), null, - ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES)); + ORIGINATING_UID); assertFalse(params1.hashCode() == params2.hashCode()); } public void testHashCode_Originating_Uid_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); - - VerificationParams params2 = new VerificationParams( - Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), 12345, new ManifestDigest(DIGEST_BYTES)); - - assertFalse(params1.hashCode() == params2.hashCode()); - } - - public void testHashCode_ManifestDigest_Failure() throws Exception { - VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, - new ManifestDigest("a different digest".getBytes())); + Uri.parse(REFERRER_STRING), 12345); assertFalse(params1.hashCode() == params2.hashCode()); } public void testHashCode_InstallerUid_Failure() throws Exception { VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI, - REFERRER, ORIGINATING_UID, MANIFEST_DIGEST); + REFERRER, ORIGINATING_UID); VerificationParams params2 = new VerificationParams( Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), - Uri.parse(REFERRER_STRING), ORIGINATING_UID, - new ManifestDigest("a different digest".getBytes())); + Uri.parse(REFERRER_STRING), ORIGINATING_UID); params2.setInstallerUid(INSTALLER_UID); assertFalse(params1.hashCode() == params2.hashCode()); diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java new file mode 100644 index 000000000000..55c00314ee63 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2015 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package android.content.res; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.DisplayMetrics; +import android.util.LocaleList; + +import java.util.Arrays; +import java.util.Locale; + +public class ResourcesLocaleResolutionTest extends AndroidTestCase { + @SmallTest + public void testGetResolvedLocale_englishIsAlwaysConsideredSupported() { + // First make sure English has no explicit assets other than the default assets + final AssetManager assets = getContext().getAssets(); + final String supportedLocales[] = assets.getNonSystemLocales(); + for (String languageTag : supportedLocales) { + if ("en-XA".equals(languageTag)) { + continue; + } + assertFalse( + "supported locales: " + Arrays.toString(supportedLocales), + "en".equals(Locale.forLanguageTag(languageTag).getLanguage())); + } + + final DisplayMetrics dm = new DisplayMetrics(); + dm.setToDefaults(); + final Configuration cfg = new Configuration(); + cfg.setToDefaults(); + // Avestan and English have no assets, but Persian does. + cfg.setLocales(LocaleList.forLanguageTags("ae,en,fa")); + Resources res = new Resources(assets, dm, cfg); + // We should get English, because it is always considered supported. + assertEquals("en", res.getResolvedLocale().toLanguageTag()); + } +} + diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java new file mode 100644 index 000000000000..c3b25a1a5a8c --- /dev/null +++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.app.Activity; +import android.os.Bundle; +import com.android.frameworks.coretests.R; + +/** + * A minimal application for DatePickerFocusTest. + */ +public class DatePickerActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.datepicker_layout); + } +} diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java new file mode 100644 index 000000000000..513e40f4bcc3 --- /dev/null +++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.app.Activity; +import android.app.Instrumentation; +import android.os.SystemClock; +import android.test.ActivityInstrumentationTestCase2; +import android.view.KeyEvent; +import android.view.View; + +import com.android.frameworks.coretests.R; + +/** + * Test {@link DatePicker} focus changes. + */ +public class DatePickerFocusTest extends ActivityInstrumentationTestCase2<DatePickerActivity> { + + private Activity mActivity; + private Instrumentation mInstrumentation; + private DatePicker mDatePicker; + + public DatePickerFocusTest() { + super(DatePickerActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mActivity = getActivity(); + mInstrumentation = getInstrumentation(); + + mDatePicker = (DatePicker) mActivity.findViewById(R.id.datePicker); + } + + /** + * Tabs (forward and backward) through the DatePicker to ensure the correct + * Views gain focus. + */ + public void testFocusTravel() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertTrue(mDatePicker.requestFocus()); + } + }); + assertViewHasFocus(com.android.internal.R.id.date_picker_header_year); + sendKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.prev); + sendKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.next); + sendKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager); + sendKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(R.id.belowPicker); + sendShiftKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager); + sendShiftKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.next); + sendShiftKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.prev); + sendShiftKey(KeyEvent.KEYCODE_TAB); + assertViewHasFocus(com.android.internal.R.id.date_picker_header_year); + } + + private void sendKey(int keycode) { + mInstrumentation.sendKeyDownUpSync(keycode); + mInstrumentation.waitForIdleSync(); + } + + private void assertViewHasFocus(final int id) throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + View view = mActivity.findViewById(id); + assertTrue(view.hasFocus()); + } + }); + } + + private void sendShiftKey(int keycode) { + final KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT); + mInstrumentation.sendKeySync(shiftDown); + + final KeyEvent keyDown = new KeyEvent(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0, + KeyEvent.META_SHIFT_ON); + mInstrumentation.sendKeySync(keyDown); + + final KeyEvent keyUp = new KeyEvent(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keycode, 0, + KeyEvent.META_SHIFT_ON); + mInstrumentation.sendKeySync(keyUp); + + final KeyEvent shiftUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT); + mInstrumentation.sendKeySync(shiftUp); + + mInstrumentation.waitForIdleSync(); + } + + /** + * Tests to ensure the keyboard can select the current year. + */ + public void testYearChoice() throws Throwable { + setKnownDate(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + View year = mDatePicker. + findViewById(com.android.internal.R.id.date_picker_header_year); + assertTrue(year.requestFocus()); + } + }); + sendKey(KeyEvent.KEYCODE_ENTER); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + View yearSelect = mDatePicker. + findViewById(com.android.internal.R.id.date_picker_year_picker); + assertEquals(yearSelect, mDatePicker.findFocus()); + } + }); + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_ENTER); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + View yearSelect = mDatePicker. + findViewById(com.android.internal.R.id.date_picker_year_picker); + assertNotSame(View.VISIBLE, yearSelect.getVisibility()); + View year = mDatePicker. + findViewById(com.android.internal.R.id.date_picker_header_year); + assertTrue(year.hasFocus()); + assertEquals(2014, mDatePicker.getYear()); + } + }); + } + + public void testArrowThroughDays() throws Throwable { + setKnownDate(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + View prev = mDatePicker.findViewById(com.android.internal.R.id.next); + prev.requestFocus(); + } + }); + sendKey(KeyEvent.KEYCODE_TAB); + // Should select the current date and the date shouldn't change + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(12, 31, 2015); + // Move right to January 24, 2016 + sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(1, 24, 2016); + // Move down to January 31, 2016 + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(1, 31, 2016); + // Move up to January 5, 2016 + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); + sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(1, 5, 2016); + // Move up to prev arrow key + sendKey(KeyEvent.KEYCODE_DPAD_UP); + assertViewHasFocus(com.android.internal.R.id.prev); + // tab back into the day-selection pager + sendKey(KeyEvent.KEYCODE_TAB); + sendKey(KeyEvent.KEYCODE_TAB); + sendKey(KeyEvent.KEYCODE_ENTER); + assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager); + assertDateIs(1, 5, 2016); + + // Move up out again, then down back into the day-selection pager. + // It should land right below the prev button (1/3/2016) + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_ENTER); + assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager); + assertDateIs(1, 3, 2016); + + // Move left to previous month (12/12/2015) + sendKey(KeyEvent.KEYCODE_DPAD_LEFT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(12, 12, 2015); + // Now make sure the start of the month works + // Move up to 12/5/2015 and right to 1/1/2016 + sendKey(KeyEvent.KEYCODE_DPAD_UP); + sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(1, 1, 2016); + // Now make sure the left key goes back to previous month (12/5/2015) + sendKey(KeyEvent.KEYCODE_DPAD_LEFT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(12, 5, 2015); + // Now go to a mismatched row (no such row on previous month) + // This moves over to 1/31/2016 and then left to 12/31/2015 + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_DPAD_RIGHT); + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_DPAD_DOWN); + sendKey(KeyEvent.KEYCODE_DPAD_LEFT); + sendKey(KeyEvent.KEYCODE_ENTER); + assertDateIs(12, 31, 2015); + } + + private void assertDateIs(int month, final int day, final int year) throws Throwable { + final int monthInt = month - 1; // months are 0-based + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertEquals(day, mDatePicker.getDayOfMonth()); + assertEquals(year, mDatePicker.getYear()); + assertEquals(monthInt, mDatePicker.getMonth()); + } + }); + } + + private void setKnownDate() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mDatePicker.updateDate(2015, 11, 31); // December 31, 2015 + } + }); + mInstrumentation.waitForIdleSync(); + } +} diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 554b6f1ba6c0..731bf42b01e2 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -344,7 +344,7 @@ <family> <font weight="400" style="normal">NanumGothic.ttf</font> </family> - <family lang="und-Qaae"> + <family lang="und-Zsye"> <font weight="400" style="normal">NotoColorEmoji.ttf</font> </family> <family> diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index da58884e3833..175c726072cc 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -163,8 +163,11 @@ public class BitmapFactory { public boolean inPremultiplied; /** - * If dither is true, the decoder will attempt to dither the decoded - * image. + * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is + * ignored. + * + * In {@link android.os.Build.VERSION_CODES#M} and below, if dither is + * true, the decoder will attempt to dither the decoded image. */ public boolean inDither; @@ -308,7 +311,11 @@ public class BitmapFactory { public boolean inInputShareable; /** - * If inPreferQualityOverSpeed is set to true, the decoder will try to + * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is + * ignored. The output will always be high quality. + * + * In {@link android.os.Build.VERSION_CODES#M} and below, if + * inPreferQualityOverSpeed is set to true, the decoder will try to * decode the reconstructed image to a higher quality even at the * expense of the decoding speed. Currently the field only affects JPEG * decode, in the case of which a more accurate, but slightly slower, @@ -347,8 +354,6 @@ public class BitmapFactory { */ public byte[] inTempStorage; - private native void requestCancel(); - /** * Flag to indicate that cancel has been called on this object. This * is useful if there's an intermediary that wants to first decode the @@ -359,16 +364,19 @@ public class BitmapFactory { public boolean mCancel; /** - * This can be called from another thread while this options object is - * inside a decode... call. Calling this will notify the decoder that - * it should cancel its operation. This is not guaranteed to cancel - * the decode, but if it does, the decoder... operation will return - * null, or if inJustDecodeBounds is true, will set outWidth/outHeight + * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this + * will not affect the decode, though it will still set mCancel. + * + * In {@link android.os.Build.VERSION_CODES#M} and below, if this can + * be called from another thread while this options object is inside + * a decode... call. Calling this will notify the decoder that it + * should cancel its operation. This is not guaranteed to cancel the + * decode, but if it does, the decoder... operation will return null, + * or if inJustDecodeBounds is true, will set outWidth/outHeight * to -1 */ public void requestCancelDecode() { mCancel = true; - requestCancel(); } } diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 1857345968fd..84ca5464f5cb 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -19,6 +19,7 @@ import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.Animator.AnimatorListener; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.ColorStateList; @@ -140,6 +141,15 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { /** Local, mutable animator set. */ private final AnimatorSet mAnimatorSet = new AnimatorSet(); + + private final ValueAnimator.AnimatorUpdateListener mUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + invalidateSelf(); + } + }; + /** * The resources against which this drawable was created. Used to attempt * to inflate animators if applyTheme() doesn't get called. @@ -201,9 +211,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { @Override public void draw(Canvas canvas) { mAnimatedVectorState.mVectorDrawable.draw(canvas); - if (isStarted()) { - invalidateSelf(); - } } @Override @@ -486,6 +493,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { * animators, or {@code null} if not available */ public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet, + @NonNull ValueAnimator.AnimatorUpdateListener updateListener, @Nullable Resources res) { // Check for uninflated animators. We can remove this after we add // support for Animator.applyTheme(). See comments in inflate(). @@ -511,6 +519,17 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { final Animator nextAnim = prepareLocalAnimator(i); builder.with(nextAnim); } + + // Setup a value animator to get animation update callbacks. + long totalDuration = animatorSet.getTotalDuration(); + ValueAnimator updateAnim = ValueAnimator.ofFloat(0f, 1f); + if (totalDuration == ValueAnimator.DURATION_INFINITE) { + updateAnim.setRepeatCount(ValueAnimator.INFINITE); + } else { + updateAnim.setDuration(totalDuration); + } + updateAnim.addUpdateListener(updateListener); + builder.with(updateAnim); } } @@ -603,7 +622,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { @NonNull private void ensureAnimatorSet() { if (!mHasAnimatorSet) { - mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes); + mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mUpdateListener, mRes); mHasAnimatorSet = true; mRes = null; } diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h index 3d4e47d84c65..914ac3d52421 100644 --- a/include/androidfw/AssetManager.h +++ b/include/androidfw/AssetManager.h @@ -100,16 +100,17 @@ public: * then on success, *cookie is set to the value corresponding to the * newly-added asset source. */ - bool addAssetPath(const String8& path, int32_t* cookie, bool appAsLib=false); + bool addAssetPath(const String8& path, int32_t* cookie, + bool appAsLib=false, bool isSystemAsset=false); bool addOverlayPath(const String8& path, int32_t* cookie); - /* + /* * Convenience for adding the standard system assets. Uses the * ANDROID_ROOT environment variable to find them. */ bool addDefaultAssets(); - /* + /* * Iterate over the asset paths in this manager. (Previously * added via addAssetPath() and addDefaultAssets().) On first call, * 'cookie' must be 0, resulting in the first cookie being returned. @@ -118,7 +119,7 @@ public: */ int32_t nextAssetPath(const int32_t cookie) const; - /* + /* * Return an asset path in the manager. 'which' must be between 0 and * countAssetPaths(). */ @@ -221,11 +222,11 @@ public: * the current data. */ bool isUpToDate(); - + /** * Get the known locales for this asset manager object. */ - void getLocales(Vector<String8>* locales) const; + void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const; /** * Generate idmap data to translate resources IDs between a package and a @@ -237,11 +238,13 @@ public: private: struct asset_path { - asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemOverlay(false) {} + asset_path() : path(""), type(kFileTypeRegular), idmap(""), + isSystemOverlay(false), isSystemAsset(false) {} String8 path; FileType type; String8 idmap; bool isSystemOverlay; + bool isSystemAsset; }; Asset* openInPathLocked(const char* fileName, AccessMode mode, diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 49b6333e8a4d..428a2b831e1a 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1552,9 +1552,9 @@ public: status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false); status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false, - bool appAsLib=false); + bool appAsLib=false, bool isSystemAsset=false); - status_t add(ResTable* src); + status_t add(ResTable* src, bool isSystemAsset=false); status_t addEmpty(const int32_t cookie); status_t getError() const; @@ -1822,9 +1822,9 @@ public: // Return the configurations (ResTable_config) that we know about void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false, - bool ignoreAndroidPackage=false) const; + bool ignoreAndroidPackage=false, bool includeSystemConfigs=true) const; - void getLocales(Vector<String8>* locales) const; + void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const; // Generate an idmap. // @@ -1860,7 +1860,7 @@ private: typedef Vector<Type*> TypeList; status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize, - bool appAsLib, const int32_t cookie, bool copyData); + bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false); ssize_t getResourcePackageIndex(uint32_t resID) const; @@ -1873,10 +1873,11 @@ private: size_t nameLen, uint32_t* outTypeSpecFlags) const; status_t parsePackage( - const ResTable_package* const pkg, const Header* const header, bool appAsLib); + const ResTable_package* const pkg, const Header* const header, + bool appAsLib, bool isSystemAsset); void print_value(const Package* pkg, const Res_value& value) const; - + mutable Mutex mLock; status_t mError; diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 8a03b94492d8..6913f43a87c3 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -176,7 +176,8 @@ AssetManager::~AssetManager(void) delete[] mVendor; } -bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAsLib) +bool AssetManager::addAssetPath( + const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) { AutoMutex _l(mLock); @@ -222,6 +223,7 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAs } delete manifestAsset; + ap.isSystemAsset = isSystemAsset; mAssetPaths.add(ap); // new paths are always added at the end @@ -233,6 +235,7 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAs // Load overlays, if any asset_path oap; for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) { + oap.isSystemAsset = isSystemAsset; mAssetPaths.add(oap); } #endif @@ -340,7 +343,7 @@ bool AssetManager::addDefaultAssets() String8 path(root); path.appendPath(kSystemAssets); - return addAssetPath(path, NULL); + return addAssetPath(path, NULL, false /* appAsLib */, true /* isSystemAsset */); } int32_t AssetManager::nextAssetPath(const int32_t cookie) const @@ -682,10 +685,10 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con ALOGV("Installing resource asset %p in to table %p\n", ass, mResources); if (sharedRes != NULL) { ALOGV("Copying existing resources for %s", ap.path.string()); - mResources->add(sharedRes); + mResources->add(sharedRes, ap.isSystemAsset); } else { ALOGV("Parsing resources for %s", ap.path.string()); - mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib); + mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib, ap.isSystemAsset); } onlyEmptyResources = false; @@ -831,11 +834,11 @@ bool AssetManager::isUpToDate() return mZipSet.isUpToDate(); } -void AssetManager::getLocales(Vector<String8>* locales) const +void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocales) const { ResTable* res = mResources; if (res != NULL) { - res->getLocales(locales); + res->getLocales(locales, includeSystemLocales); } const size_t numLocales = locales->size(); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 21b543eefa01..44f92c7bf3d6 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3080,13 +3080,16 @@ struct ResTable::Package // table that defined the package); the ones after are skins on top of it. struct ResTable::PackageGroup { - PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id, bool appAsLib) + PackageGroup( + ResTable* _owner, const String16& _name, uint32_t _id, + bool appAsLib, bool _isSystemAsset) : owner(_owner) , name(_name) , id(_id) , largestTypeId(0) , bags(NULL) , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib) + , isSystemAsset(_isSystemAsset) { } ~PackageGroup() { @@ -3178,6 +3181,10 @@ struct ResTable::PackageGroup // by having these tables in a per-package scope rather than // per-package-group. DynamicRefTable dynamicRefTable; + + // If the package group comes from a system asset. Used in + // determining non-system locales. + const bool isSystemAsset; }; struct ResTable::bag_set @@ -3572,8 +3579,9 @@ status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) { copyData); } -status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData, - bool appAsLib) { +status_t ResTable::add( + Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData, + bool appAsLib, bool isSystemAsset) { const void* data = asset->getBuffer(true); if (data == NULL) { ALOGW("Unable to get buffer of resource asset file"); @@ -3592,20 +3600,21 @@ status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bo } return addInternal(data, static_cast<size_t>(asset->getLength()), - idmapData, idmapSize, appAsLib, cookie, copyData); + idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset); } -status_t ResTable::add(ResTable* src) +status_t ResTable::add(ResTable* src, bool isSystemAsset) { mError = src->mError; - for (size_t i=0; i<src->mHeaders.size(); i++) { + for (size_t i=0; i < src->mHeaders.size(); i++) { mHeaders.add(src->mHeaders[i]); } - for (size_t i=0; i<src->mPackageGroups.size(); i++) { + for (size_t i=0; i < src->mPackageGroups.size(); i++) { PackageGroup* srcPg = src->mPackageGroups[i]; - PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, false); + PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, + false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset); for (size_t j=0; j<srcPg->packages.size(); j++) { pg->packages.add(srcPg->packages[j]); } @@ -3646,7 +3655,7 @@ status_t ResTable::addEmpty(const int32_t cookie) { } status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize, - bool appAsLib, const int32_t cookie, bool copyData) + bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset) { if (!data) { return NO_ERROR; @@ -3749,7 +3758,8 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id return (mError=BAD_TYPE); } - if (parsePackage((ResTable_package*)chunk, header, appAsLib) != NO_ERROR) { + if (parsePackage( + (ResTable_package*)chunk, header, appAsLib, isSystemAsset) != NO_ERROR) { return mError; } curPackage++; @@ -5663,7 +5673,7 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con } void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap, - bool ignoreAndroidPackage) const { + bool ignoreAndroidPackage, bool includeSystemConfigs) const { const size_t packageCount = mPackageGroups.size(); String16 android("android"); for (size_t i = 0; i < packageCount; i++) { @@ -5671,6 +5681,9 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMi if (ignoreAndroidPackage && android == packageGroup->name) { continue; } + if (!includeSystemConfigs && packageGroup->isSystemAsset) { + continue; + } const size_t typeCount = packageGroup->types.size(); for (size_t j = 0; j < typeCount; j++) { const TypeList& typeList = packageGroup->types[j]; @@ -5707,11 +5720,14 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMi } } -void ResTable::getLocales(Vector<String8>* locales) const +void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const { Vector<ResTable_config> configs; ALOGV("calling getConfigurations"); - getConfigurations(&configs); + getConfigurations(&configs, + false /* ignoreMipmap */, + false /* ignoreAndroidPackage */, + includeSystemLocales /* includeSystemConfigs */); ALOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); @@ -5937,7 +5953,7 @@ status_t ResTable::getEntry( } status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, bool appAsLib) + const Header* const header, bool appAsLib, bool isSystemAsset) { const uint8_t* base = (const uint8_t*)pkg; status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset), @@ -5985,8 +6001,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (id >= 256) { LOG_ALWAYS_FATAL("Package id out of range"); return NO_ERROR; - } else if (id == 0 || appAsLib) { - // This is a library so assign an ID + } else if (id == 0 || appAsLib || isSystemAsset) { + // This is a library or a system asset, so assign an ID id = mNextPackageId++; } @@ -6018,7 +6034,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])]; strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0])); - group = new PackageGroup(this, String16(tmpName), id, appAsLib); + group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset); if (group == NULL) { delete package; return (mError=NO_MEMORY); diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index f48c509755e3..11056d4fac97 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -108,6 +108,7 @@ ifeq (true, $(HWUI_NEW_OPS)) hwui_src_files += \ BakedOpDispatcher.cpp \ BakedOpRenderer.cpp \ + BakedOpState.cpp \ OpReorderer.cpp \ RecordingCanvas.cpp diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 0f0768f08a5e..332a2044dbfb 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -79,7 +79,9 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); - renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); + ClipRect renderTargetClip(opList.clip); + const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; + renderer.renderGlop(nullptr, clip, glop); } void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, @@ -183,7 +185,9 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer, .setTransform(Matrix4::identity(), TransformFlags::None) .setModelViewIdentityEmptyBounds() .build(); - renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop); + ClipRect renderTargetClip(opList.clip); + const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; + renderer.renderGlop(nullptr, clip, glop); } static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, @@ -224,7 +228,7 @@ enum class TextRenderType { }; static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state, - const Rect* renderClip, TextRenderType renderType) { + const ClipBase* renderClip, TextRenderType renderType) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { @@ -272,7 +276,7 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake bool forceFinish = (renderType == TextRenderType::Flush); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); - const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr; + const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; fontRenderer.renderPosText(op.paint, localOpClip, (const char*) op.glyphs, op.glyphCount, x, y, op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); @@ -287,7 +291,8 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer, const MergedBakedOpList& opList) { - const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr; + ClipRect renderTargetClip(opList.clip); + const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr; for (size_t i = 0; i < opList.count; i++) { const BakedOpState& state = *(opList.states[i]); const TextOp& op = *(static_cast<const TextOp*>(state.op)); @@ -701,14 +706,13 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR } void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { - const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr; - renderTextOp(renderer, op, state, clip, TextRenderType::Flush); + renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush); } void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) { // Note: can't trust clipSideFlags since we record with unmappedBounds == clip. // TODO: respect clipSideFlags, once we record with bounds - const Rect* renderTargetClip = &state.computedState.clipRect; + auto renderTargetClip = state.computedState.clipState; FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); fontRenderer.setFont(op.paint, SkMatrix::I()); diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index f8282dc1073a..a0d5faed270d 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -60,27 +60,67 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const } void BakedOpRenderer::endLayer() { + if (mRenderTarget.stencil) { + // if stencil was used for clipping, detach it and return it to pool + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed"); + mCaches.renderBufferCache.put(mRenderTarget.stencil); + mRenderTarget.stencil = nullptr; + } + mRenderTarget.lastStencilClip = nullptr; + mRenderTarget.offscreenBuffer->updateMeshFromRegion(); - mRenderTarget.offscreenBuffer = nullptr; + mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now. // Detach the texture from the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED"); mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId); - mRenderTarget.frameBufferId = -1; + mRenderTarget.frameBufferId = 0; } void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) { + LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0"); mRenderState.bindFramebuffer(0); setViewport(width, height); - mCaches.clearGarbage(); if (!mOpaque) { clearColorBuffer(repaintRect); } + + mRenderState.debugOverdraw(true, true); } -void BakedOpRenderer::endFrame() { +void BakedOpRenderer::endFrame(const Rect& repaintRect) { + if (CC_UNLIKELY(Properties::debugOverdraw)) { + ClipRect overdrawClip(repaintRect); + Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight); + // overdraw visualization + for (int i = 1; i <= 4; i++) { + if (i < 4) { + // nth level of overdraw tests for n+1 draws per pixel + mRenderState.stencil().enableDebugTest(i + 1, false); + } else { + // 4th level tests for 4 or higher draws per pixel + mRenderState.stencil().enableDebugTest(4, true); + } + + SkPaint paint; + paint.setColor(mCaches.getOverdrawColor(i)); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setRoundRectClipState(nullptr) + .setMeshUnitQuad() + .setFillPaint(paint, 1.0f) + .setTransform(Matrix4::identity(), TransformFlags::None) + .setModelViewMapUnitToRect(viewportRect) + .build(); + renderGlop(nullptr, &overdrawClip, glop); + } + mRenderState.stencil().disable(); + } + + mCaches.clearGarbage(); mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); @@ -128,12 +168,121 @@ Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) { return texture; } -void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { +// clears and re-fills stencil with provided rendertarget space quads, +// and then put stencil into test mode +void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, + int incrementThreshold) { + mRenderState.stencil().enableWrite(incrementThreshold); + mRenderState.stencil().clear(); + Glop glop; + GlopBuilder(mRenderState, mCaches, &glop) + .setRoundRectClipState(nullptr) + .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4) + .setFillBlack() + .setTransform(Matrix4::identity(), TransformFlags::None) + .setModelViewIdentityEmptyBounds() + .build(); + mRenderState.render(glop, mRenderTarget.orthoMatrix); + mRenderState.stencil().enableTest(incrementThreshold); +} + +void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) { + auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList; + int quadCount = rectList.getTransformedRectanglesCount(); + std::vector<Vertex> rectangleVertices; + rectangleVertices.reserve(quadCount * 4); + for (int i = 0; i < quadCount; i++) { + const TransformedRectangle& tr(rectList.getTransformedRectangle(i)); + const Matrix4& transform = tr.getTransform(); + Rect bounds = tr.getBounds(); + if (transform.rectToRect()) { + // If rectToRect, can simply map bounds before storing verts + transform.mapRect(bounds); + bounds.doIntersect(clip->rect); + if (bounds.isEmpty()) { + continue; // will be outside of scissor, skip + } + } + + rectangleVertices.push_back(Vertex{bounds.left, bounds.top}); + rectangleVertices.push_back(Vertex{bounds.right, bounds.top}); + rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom}); + rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom}); + + if (!transform.rectToRect()) { + // If not rectToRect, must map each point individually + for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) { + transform.mapPoint(cur->x, cur->y); + } + } + } + setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount()); +} + +void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) { + auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region; + + std::vector<Vertex> regionVertices; + SkRegion::Cliperator it(region, clip->rect.toSkIRect()); + while (!it.done()) { + const SkIRect& r = it.rect(); + regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop}); + regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop}); + regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom}); + regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom}); + it.next(); + } + setupStencilQuads(regionVertices, 0); +} + +void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) { + // prepare scissor / stencil mRenderState.scissor().setEnabled(clip != nullptr); if (clip) { - mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom, - clip->getWidth(), clip->getHeight()); + mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect); + } + + if (CC_LIKELY(!Properties::debugOverdraw)) { + // only modify stencil mode and content when it's not used for overdraw visualization + if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) { + // NOTE: this pointer check is only safe for non-rect clips, + // since rect clips may be created on the stack + if (mRenderTarget.lastStencilClip != clip) { + // Stencil needed, but current stencil isn't up to date + mRenderTarget.lastStencilClip = clip; + + if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) { + OffscreenBuffer* layer = mRenderTarget.offscreenBuffer; + mRenderTarget.stencil = mCaches.renderBufferCache.get( + Stencil::getLayerStencilFormat(), + layer->texture.width, layer->texture.height); + // stencil is bound + allocated - associate it with current FBO + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, mRenderTarget.stencil->getName()); + } + + if (clip->mode == ClipMode::RectangleList) { + setupStencilRectList(clip); + } else { + setupStencilRegion(clip); + } + } else { + // stencil is up to date - just need to ensure it's enabled (since an unclipped + // or scissor-only clipped op may have been drawn, disabling the stencil) + int incrementThreshold = 0; + if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { + auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList; + incrementThreshold = rectList.getTransformedRectanglesCount(); + } + mRenderState.stencil().enableTest(incrementThreshold); + } + } else { + // either scissor or no clip, so disable stencil test + mRenderState.stencil().disable(); + } } + + // dirty offscreenbuffer if (dirtyBounds && mRenderTarget.offscreenBuffer) { // register layer damage to draw-back region android::Rect dirty(dirtyBounds->left, dirtyBounds->top, @@ -142,17 +291,18 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) { } } -void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) { +void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip, + const Glop& glop) { prepareRender(dirtyBounds, clip); mRenderState.render(glop, mRenderTarget.orthoMatrix); if (!mRenderTarget.frameBufferId) mHasDrawn = true; } void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) { - prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect); + prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded()); DrawGlInfo info; - auto&& clip = state.computedState.clipRect; + auto&& clip = state.computedState.clipRect(); info.clipLeft = clip.left; info.clipTop = clip.top; info.clipRight = clip.right; diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index f158e8bb49c3..65e8b29a9ed2 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -27,6 +27,7 @@ class Caches; struct Glop; class Layer; class RenderState; +struct ClipBase; /** * Main rendering manager for a collection of work - one frame + any contained FBOs. @@ -59,7 +60,7 @@ public: Caches& caches() { return mCaches; } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect); - void endFrame(); + void endFrame(const Rect& repaintRect); OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height); void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect); void endLayer(); @@ -68,21 +69,23 @@ public: const LightInfo& getLightInfo() const { return mLightInfo; } void renderGlop(const BakedOpState& state, const Glop& glop) { - bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None; renderGlop(&state.computedState.clippedBounds, - useScissor ? &state.computedState.clipRect : nullptr, + state.computedState.getClipIfNeeded(), glop); } void renderFunctor(const FunctorOp& op, const BakedOpState& state); - void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop); + void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop); bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; } void dirtyRenderTarget(const Rect& dirtyRect); bool didDraw() const { return mHasDrawn; } private: void setViewport(uint32_t width, uint32_t height); void clearColorBuffer(const Rect& clearRect); - void prepareRender(const Rect* dirtyBounds, const Rect* clip); + void prepareRender(const Rect* dirtyBounds, const ClipBase* clip); + void setupStencilRectList(const ClipBase* clip); + void setupStencilRegion(const ClipBase* clip); + void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold); RenderState& mRenderState; Caches& mCaches; @@ -92,10 +95,23 @@ private: // render target state - setup by start/end layer/frame // only valid to use in between start/end pairs. struct { + // If not drawing to a layer: fbo = 0, offscreenBuffer = null, + // Otherwise these refer to currently painting layer's state GLuint frameBufferId = 0; OffscreenBuffer* offscreenBuffer = nullptr; + + // Used when drawing to a layer and using stencil clipping. otherwise null. + RenderBuffer* stencil = nullptr; + + // value representing the ClipRectList* or ClipRegion* currently stored in + // the stencil of the current render target + const ClipBase* lastStencilClip = nullptr; + + // Size of renderable region in current render target - for layers, may not match actual + // bounds of FBO texture. offscreenBuffer->texture has this information. uint32_t viewportWidth = 0; uint32_t viewportHeight = 0; + Matrix4 orthoMatrix; } mRenderTarget; diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp new file mode 100644 index 000000000000..e6b943a606d5 --- /dev/null +++ b/libs/hwui/BakedOpState.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BakedOpState.h" + +#include "ClipArea.h" + +namespace android { +namespace uirenderer { + +ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp, bool expandForStroke) { + // resolvedMatrix = parentMatrix * localMatrix + transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix); + + // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect) + clippedBounds = recordedOp.unmappedBounds; + if (CC_UNLIKELY(expandForStroke)) { + // account for non-hairline stroke + clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f); + } + transform.mapRect(clippedBounds); + if (CC_UNLIKELY(expandForStroke + && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { + // account for hairline stroke when stroke may be < 1 scaled pixel + // Non translate || strokeWidth < 1 is conservative, but will cover all cases + clippedBounds.outset(0.5f); + } + + // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) + clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator, + recordedOp.localClip, *(snapshot.transform)); + LOG_ALWAYS_FATAL_IF(!clipState, "must clip!"); + + const Rect& clipRect = clipState->rect; + if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) { + // Rejected based on either empty clip, or bounds not intersecting with clip + if (clipState) { + allocator.rewindIfLastAlloc(clipState); + clipState = nullptr; + } + clippedBounds.setEmpty(); + } else { + // Not rejected! compute true clippedBounds and clipSideFlags + if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left; + if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top; + if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right; + if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom; + clippedBounds.doIntersect(clipRect); + } +} + +ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot) { + transform = *snapshot.transform; + + // Since the op doesn't have known bounds, we conservatively set the mapped bounds + // to the current clipRect, and clipSideFlags to Full. + clipState = snapshot.mutateClipArea().serializeClip(allocator); + LOG_ALWAYS_FATAL_IF(!clipState, "clipState required"); + clippedBounds = clipState->rect; + transform.mapRect(clippedBounds); + clipSideFlags = OpClipSideFlags::Full; +} + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h index b12c0c970352..9df4e3aa5442 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -52,89 +52,35 @@ struct MergedBakedOpList { */ class ResolvedRenderState { public: - // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates - ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) { - /* TODO: benchmark a fast path for translate-only matrices, such as: - if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate - && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) { - float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX(); - float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY(); - transform.loadTranslate(translateX, translateY, 0); - - // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) - clipRect = recordedOp.localClipRect; - clipRect.translate(translateX, translateY); - clipRect.doIntersect(snapshot.getClipRect()); - clipRect.snapToPixelBoundaries(); - - // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect) - clippedBounds = recordedOp.unmappedBounds; - clippedBounds.translate(translateX, translateY); - } ... */ - - // resolvedMatrix = parentMatrix * localMatrix - transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix); - - // resolvedClipRect = intersect(parentMatrix * localClip, parentClip) - clipRect = recordedOp.localClipRect; - snapshot.transform->mapRect(clipRect); - clipRect.doIntersect(snapshot.getRenderTargetClip()); - clipRect.snapToPixelBoundaries(); - - // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect) - clippedBounds = recordedOp.unmappedBounds; - if (CC_UNLIKELY(expandForStroke)) { - // account for non-hairline stroke - clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f); - } - transform.mapRect(clippedBounds); - if (CC_UNLIKELY(expandForStroke - && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) { - // account for hairline stroke when stroke may be < 1 scaled pixel - // Non translate || strokeWidth < 1 is conservative, but will cover all cases - clippedBounds.outset(0.5f); - } + ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp, bool expandForStroke); - if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left; - if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top; - if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right; - if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom; - clippedBounds.doIntersect(clipRect); - - /** - * TODO: once we support complex clips, we may want to reject to avoid that work where - * possible. Should we: - * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp) - * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection - * and early return null in one place. - */ - } - - /** - * Constructor for unbounded ops without transform/clip (namely shadows) - * - * Since the op doesn't have known bounds, we conservatively set the mapped bounds - * to the current clipRect, and clipSideFlags to Full. - */ - ResolvedRenderState(const Snapshot& snapshot) { - transform = *snapshot.transform; - clipRect = snapshot.getRenderTargetClip(); - clippedBounds = clipRect; - transform.mapRect(clippedBounds); - clipSideFlags = OpClipSideFlags::Full; - } + // Constructor for unbounded ops without transform/clip (namely shadows) + ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot); Rect computeLocalSpaceClip() const { Matrix4 inverse; inverse.loadInverse(transform); - Rect outClip(clipRect); + Rect outClip(clipRect()); inverse.mapRect(outClip); return outClip; } Matrix4 transform; - Rect clipRect; + const Rect& clipRect() const { + return clipState->rect; + } + bool requiresClip() const { + return clipSideFlags != OpClipSideFlags::None + || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); + } + + // returns the clip if it's needed to draw the operation, otherwise nullptr + const ClipBase* getClipIfNeeded() const { + return requiresClip() ? clipState : nullptr; + } + const ClipBase* clipState = nullptr; int clipSideFlags = 0; Rect clippedBounds; }; @@ -147,8 +93,9 @@ public: class BakedOpState { public: static BakedOpState* tryConstruct(LinearAllocator& allocator, - const Snapshot& snapshot, const RecordedOp& recordedOp) { - BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false); + Snapshot& snapshot, const RecordedOp& recordedOp) { + BakedOpState* bakedState = new (allocator) BakedOpState( + allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); @@ -165,13 +112,13 @@ public: }; static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, - const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { + Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = new (allocator) BakedOpState( - snapshot, recordedOp, expandForStroke); + allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); @@ -181,11 +128,11 @@ public: } static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, - const Snapshot& snapshot, const ShadowOp* shadowOpPtr) { + Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (snapshot.getRenderTargetClip().isEmpty()) return nullptr; // clip isn't empty, so construct the op - return new (allocator) BakedOpState(snapshot, shadowOpPtr); + return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr); } static void* operator new(size_t size, LinearAllocator& allocator) { @@ -202,15 +149,16 @@ public: const RecordedOp* op; private: - BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) - : computedState(snapshot, recordedOp, expandForStroke) + BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, + const RecordedOp& recordedOp, bool expandForStroke) + : computedState(allocator, snapshot, recordedOp, expandForStroke) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(&recordedOp) {} - BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr) - : computedState(snapshot) + BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) + : computedState(allocator, snapshot) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index cf76e6be46c4..cf2726b5f530 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -45,6 +45,21 @@ CanvasState::~CanvasState() { } } +void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) { + if (mWidth != viewportWidth || mHeight != viewportHeight) { + mWidth = viewportWidth; + mHeight = viewportHeight; + mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight); + mCanvas.onViewportInitialized(); + } + + freeAllSnapshots(); + mSnapshot = allocSnapshot(&mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot->setRelativeLightCenter(Vector3()); + mSaveCount = 1; +} + void CanvasState::initializeSaveStack( int viewportWidth, int viewportHeight, float clipLeft, float clipTop, diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h index 4709ef41915f..b9e87ae5595d 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -80,6 +80,12 @@ public: * Initializes the first snapshot, computing the projection matrix, * and stores the dimensions of the render target. */ + void initializeRecordingSaveStack(int viewportWidth, int viewportHeight); + + /** + * Initializes the first snapshot, computing the projection matrix, + * and stores the dimensions of the render target. + */ void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop, float clipRight, float clipBottom, const Vector3& lightCenter); @@ -168,6 +174,7 @@ private: void freeAllSnapshots(); /// indicates that the clip has been changed since the last time it was consumed + // TODO: delete when switching to HWUI_NEW_OPS bool mDirtyClip; /// Dimensions of the drawing surface diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index 5f166cafd01c..160090dcd8cc 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -15,10 +15,11 @@ */ #include "ClipArea.h" +#include "utils/LinearAllocator.h" + #include <SkPath.h> #include <limits> - -#include "Rect.h" +#include <type_traits> namespace android { namespace uirenderer { @@ -171,12 +172,18 @@ SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { return rectangleListAsRegion; } +void RectangleList::transform(const Matrix4& transform) { + for (int index = 0; index < mTransformedRectanglesCount; index++) { + mTransformedRectangles[index].transform(transform); + } +} + /* * ClipArea */ ClipArea::ClipArea() - : mMode(Mode::Rectangle) { + : mMode(ClipMode::Rectangle) { } /* @@ -184,39 +191,44 @@ ClipArea::ClipArea() */ void ClipArea::setViewportDimensions(int width, int height) { + mPostViewportClipObserved = false; mViewportBounds.set(0, 0, width, height); mClipRect = mViewportBounds; } void ClipArea::setEmpty() { - mMode = Mode::Rectangle; + onClipUpdated(); + mMode = ClipMode::Rectangle; mClipRect.setEmpty(); mClipRegion.setEmpty(); mRectangleList.setEmpty(); } void ClipArea::setClip(float left, float top, float right, float bottom) { - mMode = Mode::Rectangle; + onClipUpdated(); + mMode = ClipMode::Rectangle; mClipRect.set(left, top, right, bottom); mClipRegion.setEmpty(); } void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { + onClipUpdated(); switch (mMode) { - case Mode::Rectangle: + case ClipMode::Rectangle: rectangleModeClipRectWithTransform(r, transform, op); break; - case Mode::RectangleList: + case ClipMode::RectangleList: rectangleListModeClipRectWithTransform(r, transform, op); break; - case Mode::Region: + case ClipMode::Region: regionModeClipRectWithTransform(r, transform, op); break; } } void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { + onClipUpdated(); enterRegionMode(); mClipRegion.op(region, op); onClipRegionUpdated(); @@ -224,6 +236,7 @@ void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { + onClipUpdated(); SkMatrix skTransform; transform->copyTo(skTransform); SkPath transformed; @@ -241,7 +254,7 @@ void ClipArea::enterRectangleMode() { // Entering rectangle mode discards any // existing clipping information from the other modes. // The only way this occurs is by a clip setting operation. - mMode = Mode::Rectangle; + mMode = ClipMode::Rectangle; } void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, @@ -276,8 +289,8 @@ void ClipArea::enterRectangleListMode() { // Is is only legal to enter rectangle list mode from // rectangle mode, since rectangle list mode cannot represent // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == Mode::Rectangle); - mMode = Mode::RectangleList; + ALOG_ASSERT(mMode == ClipMode::Rectangle); + mMode = ClipMode::RectangleList; mRectangleList.set(mClipRect, Matrix4::identity()); } @@ -295,12 +308,11 @@ void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, */ void ClipArea::enterRegionMode() { - Mode oldMode = mMode; - mMode = Mode::Region; - if (oldMode != Mode::Region) { - if (oldMode == Mode::Rectangle) { - mClipRegion.setRect(mClipRect.left, mClipRect.top, - mClipRect.right, mClipRect.bottom); + ClipMode oldMode = mMode; + mMode = ClipMode::Region; + if (oldMode != ClipMode::Region) { + if (oldMode == ClipMode::Rectangle) { + mClipRegion.setRect(mClipRect.toSkIRect()); } else { mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); onClipRegionUpdated(); @@ -330,5 +342,172 @@ void ClipArea::onClipRegionUpdated() { } } +/** + * Clip serialization + */ + +const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { + if (!mPostViewportClipObserved) { + // Only initial clip-to-viewport observed, so no serialization of clip necessary + return nullptr; + } + + static_assert(std::is_trivially_destructible<Rect>::value, + "expect Rect to be trivially destructible"); + static_assert(std::is_trivially_destructible<RectangleList>::value, + "expect RectangleList to be trivially destructible"); + + if (mLastSerialization == nullptr) { + switch (mMode) { + case ClipMode::Rectangle: + mLastSerialization = allocator.create<ClipRect>(mClipRect); + break; + case ClipMode::RectangleList: + mLastSerialization = allocator.create<ClipRectList>(mRectangleList); + break; + case ClipMode::Region: + mLastSerialization = allocator.create<ClipRegion>(mClipRegion); + break; + } + } + return mLastSerialization; +} + +inline static const Rect& getRect(const ClipBase* scb) { + return reinterpret_cast<const ClipRect*>(scb)->rect; +} + +inline static const RectangleList& getRectList(const ClipBase* scb) { + return reinterpret_cast<const ClipRectList*>(scb)->rectList; +} + +inline static const SkRegion& getRegion(const ClipBase* scb) { + return reinterpret_cast<const ClipRegion*>(scb)->region; +} + +// Conservative check for too many rectangles to fit in rectangle list. +// For simplicity, doesn't account for rect merging +static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) { + int currentRectCount = clipArea.isRectangleList() + ? clipArea.getRectangleList().getTransformedRectanglesCount() + : 1; + int recordedRectCount = (scb->mode == ClipMode::RectangleList) + ? getRectList(scb).getTransformedRectanglesCount() + : 1; + return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles; +} + +const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, + const ClipBase* recordedClip, const Matrix4& recordedClipTransform) { + // if no recordedClip passed, just serialize current state + if (!recordedClip) return serializeClip(allocator); + + if (!mLastResolutionResult + || recordedClip != mLastResolutionClip + || recordedClipTransform != mLastResolutionTransform) { + mLastResolutionClip = recordedClip; + mLastResolutionTransform = recordedClipTransform; + + if (CC_LIKELY(mMode == ClipMode::Rectangle + && recordedClip->mode == ClipMode::Rectangle + && recordedClipTransform.rectToRect())) { + // common case - result is a single rectangle + auto rectClip = allocator.create<ClipRect>(getRect(recordedClip)); + recordedClipTransform.mapRect(rectClip->rect); + rectClip->rect.doIntersect(mClipRect); + mLastResolutionResult = rectClip; + } else if (CC_UNLIKELY(mMode == ClipMode::Region + || recordedClip->mode == ClipMode::Region + || cannotFitInRectangleList(*this, recordedClip))) { + // region case + SkRegion other; + switch (recordedClip->mode) { + case ClipMode::Rectangle: + if (CC_LIKELY(recordedClipTransform.rectToRect())) { + // simple transform, skip creating SkPath + Rect resultClip(getRect(recordedClip)); + recordedClipTransform.mapRect(resultClip); + other.setRect(resultClip.toSkIRect()); + } else { + SkPath transformedRect = pathFromTransformedRectangle(getRect(recordedClip), + recordedClipTransform); + other.setPath(transformedRect, createViewportRegion()); + } + break; + case ClipMode::RectangleList: { + RectangleList transformedList(getRectList(recordedClip)); + transformedList.transform(recordedClipTransform); + other = transformedList.convertToRegion(createViewportRegion()); + break; + } + case ClipMode::Region: + other = getRegion(recordedClip); + + // TODO: handle non-translate transforms properly! + other.translate(recordedClipTransform.getTranslateX(), + recordedClipTransform.getTranslateY()); + } + + ClipRegion* regionClip = allocator.create<ClipRegion>(); + switch (mMode) { + case ClipMode::Rectangle: + regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); + break; + case ClipMode::RectangleList: + regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), + other, SkRegion::kIntersect_Op); + break; + case ClipMode::Region: + regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); + break; + } + regionClip->rect.set(regionClip->region.getBounds()); + mLastResolutionResult = regionClip; + } else { + auto rectListClip = allocator.create<ClipRectList>(mRectangleList); + auto&& rectList = rectListClip->rectList; + if (mMode == ClipMode::Rectangle) { + rectList.set(mClipRect, Matrix4::identity()); + } + + if (recordedClip->mode == ClipMode::Rectangle) { + rectList.intersectWith(getRect(recordedClip), recordedClipTransform); + } else { + const RectangleList& other = getRectList(recordedClip); + for (int i = 0; i < other.getTransformedRectanglesCount(); i++) { + auto&& tr = other.getTransformedRectangle(i); + Matrix4 totalTransform(recordedClipTransform); + totalTransform.multiply(tr.getTransform()); + rectList.intersectWith(tr.getBounds(), totalTransform); + } + } + rectListClip->rect = rectList.calculateBounds(); + mLastResolutionResult = rectListClip; + } + } + return mLastResolutionResult; +} + +void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) { + if (!clip) return; // nothing to do + + if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) { + clipRectWithTransform(getRect(clip), &transform, SkRegion::kIntersect_Op); + } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { + auto&& rectList = getRectList(clip); + for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) { + auto&& tr = rectList.getTransformedRectangle(i); + Matrix4 totalTransform(transform); + totalTransform.multiply(tr.getTransform()); + clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op); + } + } else { + SkRegion region(getRegion(clip)); + // TODO: handle non-translate transforms properly! + region.translate(transform.getTranslateX(), transform.getTranslateY()); + clipRegion(region, SkRegion::kIntersect_Op); + } +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 268301c62fc9..479796db042a 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -16,15 +16,17 @@ #ifndef CLIPAREA_H #define CLIPAREA_H -#include <SkRegion.h> - #include "Matrix.h" #include "Rect.h" #include "utils/Pair.h" +#include <SkRegion.h> + namespace android { namespace uirenderer { +class LinearAllocator; + Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform); class TransformedRectangle { @@ -50,6 +52,12 @@ public: return mTransform; } + void transform(const Matrix4& transform) { + Matrix4 t; + t.loadMultiply(transform, mTransform); + mTransform = t; + } + private: Rect mBounds; Matrix4 mTransform; @@ -66,27 +74,62 @@ public: void setEmpty(); void set(const Rect& bounds, const Matrix4& transform); bool intersectWith(const Rect& bounds, const Matrix4& transform); + void transform(const Matrix4& transform); SkRegion convertToRegion(const SkRegion& clip) const; Rect calculateBounds() const; -private: enum { kMaxTransformedRectangles = 5 }; +private: int mTransformedRectanglesCount; TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles]; }; -class ClipArea { -private: - enum class Mode { - Rectangle, - Region, - RectangleList - }; +enum class ClipMode { + Rectangle, + RectangleList, + // region and path - intersected. if either is empty, don't use + Region +}; + +struct ClipBase { + ClipBase(ClipMode mode) + : mode(mode) {} + ClipBase(const Rect& rect) + : mode(ClipMode::Rectangle) + , rect(rect) {} + const ClipMode mode; + // Bounds of the clipping area, used to define the scissor, and define which + // portion of the stencil is updated/used + Rect rect; +}; + +struct ClipRect : ClipBase { + ClipRect(const Rect& rect) + : ClipBase(rect) {} +}; + +struct ClipRectList : ClipBase { + ClipRectList(const RectangleList& rectList) + : ClipBase(ClipMode::RectangleList) + , rectList(rectList) {} + RectangleList rectList; +}; + +struct ClipRegion : ClipBase { + ClipRegion(const SkRegion& region) + : ClipBase(ClipMode::Region) + , region(region) {} + ClipRegion() + : ClipBase(ClipMode::Region) {} + SkRegion region; +}; + +class ClipArea { public: ClipArea(); @@ -117,17 +160,22 @@ public: } bool isRegion() const { - return Mode::Region == mMode; + return ClipMode::Region == mMode; } bool isSimple() const { - return mMode == Mode::Rectangle; + return mMode == ClipMode::Rectangle; } bool isRectangleList() const { - return mMode == Mode::RectangleList; + return mMode == ClipMode::RectangleList; } + const ClipBase* serializeClip(LinearAllocator& allocator); + const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, + const ClipBase* recordedClip, const Matrix4& recordedClipTransform); + void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); + private: void enterRectangleMode(); void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); @@ -145,6 +193,13 @@ private: void ensureClipRegion(); void onClipRegionUpdated(); + // Called by every state modifying public method. + void onClipUpdated() { + mPostViewportClipObserved = true; + mLastSerialization = nullptr; + mLastResolutionResult = nullptr; + } + SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); } @@ -155,7 +210,22 @@ private: pathAsRegion.setPath(path, createViewportRegion()); } - Mode mMode; + ClipMode mMode; + bool mPostViewportClipObserved = false; + + /** + * If mLastSerialization is non-null, it represents an already serialized copy + * of the current clip state. If null, it has not been computed. + */ + const ClipBase* mLastSerialization = nullptr; + + /** + * This pair of pointers is a single entry cache of most recently seen + */ + const ClipBase* mLastResolutionResult = nullptr; + const ClipBase* mLastResolutionClip = nullptr; + Matrix4 mLastResolutionTransform; + Rect mViewportBounds; Rect mClipRect; SkRegion mClipRegion; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index ff4dc4aef94a..99944985cda8 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -47,6 +47,7 @@ namespace uirenderer { #if HWUI_NEW_OPS class BakedOpState; class BakedOpRenderer; +struct ClipBase; #else class OpenGLRenderer; #endif @@ -57,7 +58,7 @@ public: #if HWUI_NEW_OPS BakedOpRenderer* renderer, const BakedOpState* bakedState, - const Rect* clip, + const ClipBase* clip, #else OpenGLRenderer* renderer, #endif @@ -81,7 +82,7 @@ public: #if HWUI_NEW_OPS BakedOpRenderer* renderer; const BakedOpState* bakedState; - const Rect* clip; + const ClipBase* clip; #else OpenGLRenderer* renderer; #endif diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h index bcf819eb42e0..e72f39621d57 100644 --- a/libs/hwui/Glop.h +++ b/libs/hwui/Glop.h @@ -64,7 +64,7 @@ namespace TransformFlags { // Canvas transform isn't applied to the mesh at draw time, //since it's already built in. - MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove + MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS }; }; diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp index 2507ff377133..45fc16cc3136 100644 --- a/libs/hwui/GlopBuilder.cpp +++ b/libs/hwui/GlopBuilder.cpp @@ -78,7 +78,7 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementC mOutGlop->mesh.vertices = { vbo, VertexAttribFlags::TextureCoord, - nullptr, nullptr, nullptr, + nullptr, (const void*) kMeshTextureOffset, nullptr, kTextureVertexStride }; mOutGlop->mesh.elementCount = elementCount; return *this; diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp index ad9559ffdf9f..11b2c8a8c282 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -460,7 +460,7 @@ void OpReorderer::deferNodePropsAndOps(RenderNode& node) { deferBeginLayerOp(*new (mAllocator) BeginLayerOp( saveLayerBounds, Matrix4::identity(), - saveLayerBounds, + nullptr, // no record-time clip - need only respect defer-time one &saveLayerPaint)); deferNodeOps(node); deferEndLayerOp(*new (mAllocator) EndLayerOp()); @@ -604,7 +604,7 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) { mCanvasState.getLocalClipBounds(), mCanvasState.currentSnapshot()->getRelativeLightCenter()); BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct( - mAllocator, *mCanvasState.currentSnapshot(), shadowOp); + mAllocator, *mCanvasState.writableSnapshot(), shadowOp); if (CC_LIKELY(bakedOpState)) { currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow); } @@ -681,10 +681,10 @@ void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) { if (op.renderNode->nothingToDraw()) return; int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); - // apply state from RecordedOp + // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) + mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip, + *mCanvasState.currentSnapshot()->transform); mCanvasState.concatMatrix(op.localMatrix); - mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top, - op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op); // then apply state from node properties, and defer ops deferNodePropsAndOps(*op.renderNode); @@ -706,7 +706,7 @@ void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( - mAllocator, *mCanvasState.currentSnapshot(), op, strokeBehavior); + mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); } @@ -769,7 +769,7 @@ void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) { const OvalOp* resolvedOp = new (mAllocator) OvalOp( unmappedBounds, op.localMatrix, - op.localClipRect, + op.localClip, op.paint); deferOvalOp(*resolvedOp); } @@ -829,7 +829,7 @@ void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) { const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp( Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), op.localMatrix, - op.localClipRect, + op.localClip, op.paint, *op.rx, *op.ry); deferRoundRectOp(*resolvedOp); } @@ -953,7 +953,7 @@ void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) { LayerOp* drawLayerOp = new (mAllocator) LayerOp( beginLayerOp.unmappedBounds, beginLayerOp.localMatrix, - beginLayerOp.localClipRect, + beginLayerOp.localClip, beginLayerOp.paint, &mLayerReorderers[finishedLayerIndex].offscreenBuffer); BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp); diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h index dbbce8b15170..4e9b5e6a91f1 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/OpReorderer.h @@ -192,7 +192,7 @@ public: const LayerReorderer& fbo0 = mLayerReorderers[0]; renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers); - renderer.endFrame(); + renderer.endFrame(fbo0.repaintRect); } void dump() const { @@ -223,7 +223,7 @@ private: LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; } BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) { - return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp); + return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp); } // should always be surrounded by a save/restore pair, and not called if DisplayList is null diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index cfdd0d211dac..1971530d2caa 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -33,6 +33,7 @@ class SkPaint; namespace android { namespace uirenderer { +struct ClipBase; class OffscreenBuffer; class RenderNode; struct Vertex; @@ -91,10 +92,10 @@ namespace RecordedOpId { static_assert(RecordedOpId::ArcOp == 0, "First index must be zero for LUTs to work"); -#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint -#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect -#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, paint) -#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, nullptr) +#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint +#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip +#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, paint) +#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr) struct RecordedOp { /* ID from RecordedOpId - generally used for jumping into function tables */ @@ -106,8 +107,8 @@ struct RecordedOp { /* transform in recording space (vs DisplayList origin) */ const Matrix4 localMatrix; - /* clip in recording space */ - const Rect localClipRect; + /* clip in recording space - nullptr if not clipped */ + const ClipBase* localClip; /* optional paint, stored in base object to simplify merging logic */ const SkPaint* paint; @@ -116,7 +117,7 @@ protected: : opId(opId) , unmappedBounds(unmappedBounds) , localMatrix(localMatrix) - , localClipRect(localClipRect) + , localClip(localClip) , paint(paint) {} }; @@ -187,9 +188,9 @@ struct BitmapRectOp : RecordedOp { }; struct CirclePropsOp : RecordedOp { - CirclePropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint, + CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, float* x, float* y, float* radius) - : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClipRect, paint) + : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint) , x(x) , y(y) , radius(radius) {} @@ -259,9 +260,9 @@ struct RoundRectOp : RecordedOp { }; struct RoundRectPropsOp : RecordedOp { - RoundRectPropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint, + RoundRectPropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint, float* left, float* top, float* right, float* bottom, float *rx, float *ry) - : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClipRect, paint) + : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClip, paint) , left(left) , top(top) , right(right) @@ -286,12 +287,13 @@ struct RoundRectPropsOp : RecordedOp { */ struct ShadowOp : RecordedOp { ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath, - const Rect& clipRect, const Vector3& lightCenter) - : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr) + const Rect& localClipRect, const Vector3& lightCenter) + : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), nullptr, nullptr) , shadowMatrixXY(casterOp.localMatrix) , shadowMatrixZ(casterOp.localMatrix) , casterAlpha(casterAlpha) , casterPath(casterPath) + , localClipRect(localClipRect) , lightCenter(lightCenter) { const RenderNode& node = *casterOp.renderNode; node.applyViewPropertyTransforms(shadowMatrixXY, false); @@ -301,6 +303,7 @@ struct ShadowOp : RecordedOp { Matrix4 shadowMatrixZ; const float casterAlpha; const SkPath* casterPath; + const Rect localClipRect; const Vector3 lightCenter; }; @@ -374,7 +377,7 @@ struct BeginLayerOp : RecordedOp { */ struct EndLayerOp : RecordedOp { EndLayerOp() - : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), Rect(), nullptr) {} + : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {} }; /** @@ -388,13 +391,13 @@ struct LayerOp : RecordedOp { LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle) : SUPER_PAINTLESS(LayerOp) , layerHandle(layerHandle) - , alpha(paint->getAlpha() / 255.0f) + , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f) , mode(PaintUtils::getXfermodeDirect(paint)) - , colorFilter(paint->getColorFilter()) + , colorFilter(paint ? paint->getColorFilter() : nullptr) , destroy(true) {} LayerOp(RenderNode& node) - : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr) + : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr) , layerHandle(node.getLayerHandle()) , alpha(node.properties().layerProperties().alpha() / 255.0f) , mode(node.properties().layerProperties().xferMode()) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index f75d8d4f6479..f7f6caff22b6 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -39,7 +39,7 @@ void RecordingCanvas::reset(int width, int height) { "prepareDirty called a second time during a recording!"); mDisplayList = new DisplayList(); - mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3()); + mState.initializeRecordingSaveStack(width, height); mDeferredBarrierType = DeferredBarrierType::InOrder; mState.setDirtyClip(false); @@ -155,6 +155,8 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, return saveValue; } + auto previousClip = getRecordedClip(); // note: done while snapshot == previous + snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight()); snapshot.transform->loadTranslate(-untransformedBounds.left, -untransformedBounds.top, 0.0f); @@ -167,7 +169,7 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, addOp(new (alloc()) BeginLayerOp( Rect(left, top, right, bottom), *previous.transform, // transform to *draw* with - previous.getRenderTargetClip(), // clip to *draw* with + previousClip, // clip to *draw* with refPaint(paint))); return saveValue; @@ -229,11 +231,10 @@ void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { } void RecordingCanvas::drawPaint(const SkPaint& paint) { - // TODO: more efficient recording? addOp(new (alloc()) RectOp( - mState.getRenderTargetClipBounds(), + mState.getRenderTargetClipBounds(), // OK, since we've not passed transform Matrix4::identity(), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint))); } @@ -253,7 +254,7 @@ void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPa addOp(new (alloc()) PointsOp( calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); } @@ -264,7 +265,7 @@ void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPai addOp(new (alloc()) LinesOp( calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform, - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount)); } @@ -272,7 +273,7 @@ void RecordingCanvas::drawRect(float left, float top, float right, float bottom, addOp(new (alloc()) RectOp( Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint))); } @@ -305,7 +306,7 @@ void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const addOp(new (alloc()) SimpleRectsOp( Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(paint), rectData, vertexCount)); } @@ -339,7 +340,7 @@ void RecordingCanvas::drawRoundRect(float left, float top, float right, float bo addOp(new (alloc()) RoundRectOp( Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), rx, ry)); } @@ -358,7 +359,7 @@ void RecordingCanvas::drawRoundRect( refBitmapsInShader(paint->value.getShader()); addOp(new (alloc()) RoundRectPropsOp( *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), &paint->value, &left->value, &top->value, &right->value, &bottom->value, &rx->value, &ry->value)); @@ -380,7 +381,7 @@ void RecordingCanvas::drawCircle( refBitmapsInShader(paint->value.getShader()); addOp(new (alloc()) CirclePropsOp( *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), &paint->value, &x->value, &y->value, &radius->value)); } @@ -390,7 +391,7 @@ void RecordingCanvas::drawOval(float left, float top, float right, float bottom, addOp(new (alloc()) OvalOp( Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint))); } @@ -399,7 +400,7 @@ void RecordingCanvas::drawArc(float left, float top, float right, float bottom, addOp(new (alloc()) ArcOp( Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), startAngle, sweepAngle, useCenter)); } @@ -408,7 +409,7 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { addOp(new (alloc()) PathOp( Rect(path.getBounds()), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), refPath(&path))); } @@ -459,7 +460,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr addOp(new (alloc()) BitmapRectOp( Rect(dstLeft, dstTop, dstRight, dstBottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(paint), refBitmap(bitmap), Rect(srcLeft, srcTop, srcRight, srcBottom))); } @@ -471,7 +472,7 @@ void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int addOp(new (alloc()) BitmapMeshOp( calcBoundsOfPoints(vertices, vertexCount * 2), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight, refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex refBuffer<int>(colors, vertexCount))); // 1 color per vertex @@ -483,7 +484,7 @@ void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_p addOp(new (alloc()) PatchOp( Rect(dstLeft, dstTop, dstRight, dstBottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(paint), refBitmap(bitmap), refPatch(&patch))); } @@ -499,7 +500,7 @@ void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, i addOp(new (alloc()) TextOp( Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), glyphs, positions, glyphCount, x, y)); drawTextDecorations(x, y, totalAdvance, paint); } @@ -509,9 +510,9 @@ void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, con if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; glyphs = refBuffer<glyph_t>(glyphs, glyphCount); addOp(new (alloc()) TextOnPathOp( - mState.getRenderTargetClipBounds(), // TODO: explicitly define bounds + mState.getLocalClipBounds(), // TODO: explicitly define bounds *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset)); } @@ -519,7 +520,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { addOp(new (alloc()) BitmapOp( Rect(bitmap->width(), bitmap->height()), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), refPaint(paint), refBitmap(*bitmap))); } @@ -528,7 +529,7 @@ void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { RenderNodeOp* op = new (alloc()) RenderNodeOp( Rect(stagingProps.getWidth(), stagingProps.getHeight()), *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), renderNode); int opIndex = addOp(op); int childIndex = mDisplayList->addChild(op); @@ -554,16 +555,16 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { addOp(new (alloc()) TextureLayerOp( Rect(layer->getWidth(), layer->getHeight()), totalTransform, - mState.getRenderTargetClipBounds(), + getRecordedClip(), layer)); } void RecordingCanvas::callDrawGLFunction(Functor* functor) { mDisplayList->functors.push_back(functor); addOp(new (alloc()) FunctorOp( - mState.getRenderTargetClipBounds(), // TODO: explicitly define bounds + mState.getLocalClipBounds(), // TODO: explicitly define bounds *(mState.currentSnapshot()->transform), - mState.getRenderTargetClipBounds(), + getRecordedClip(), functor)); } diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 470f9ecb5024..1a2ac97f5364 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -36,6 +36,7 @@ namespace android { namespace uirenderer { +struct ClipBase; class DeferredLayerUpdater; struct RecordedOp; @@ -199,6 +200,9 @@ public: virtual bool drawTextAbsolutePos() const override { return false; } private: + const ClipBase* getRecordedClip() { + return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc()); + } void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 194aa5735a7e..5fac3a1497c0 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -167,6 +167,7 @@ public: const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); } bool clipIsSimple() const { return mClipArea->isSimple(); } const ClipArea& getClipArea() const { return *mClipArea; } + ClipArea& mutateClipArea() { return *mClipArea; } /** * Resets the clip to the specified rect. diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp index 95dcd18867d9..61dd8c3200a4 100644 --- a/libs/hwui/renderstate/Scissor.cpp +++ b/libs/hwui/renderstate/Scissor.cpp @@ -15,6 +15,8 @@ */ #include "renderstate/Scissor.h" +#include "Rect.h" + #include <utils/Log.h> namespace android { @@ -71,6 +73,26 @@ bool Scissor::set(GLint x, GLint y, GLint width, GLint height) { return false; } +void Scissor::set(int viewportHeight, const Rect& clip) { + // transform to Y-flipped GL space, and prevent negatives + GLint x = std::max(0, (int)clip.left); + GLint y = std::max(0, viewportHeight - (int)clip.bottom); + GLint width = std::max(0, ((int)clip.right) - x); + GLint height = std::max(0, (viewportHeight - (int)clip.top) - y); + + if (x != mScissorX + || y != mScissorY + || width != mScissorWidth + || height != mScissorHeight) { + glScissor(x, y, width, height); + + mScissorX = x; + mScissorY = y; + mScissorWidth = width; + mScissorHeight = height; + } +} + void Scissor::reset() { mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; } diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h index b37ec583686f..f30224470059 100644 --- a/libs/hwui/renderstate/Scissor.h +++ b/libs/hwui/renderstate/Scissor.h @@ -22,11 +22,14 @@ namespace android { namespace uirenderer { +class Rect; + class Scissor { friend class RenderState; public: bool setEnabled(bool enabled); bool set(GLint x, GLint y, GLint width, GLint height); + void set(int viewportHeight, const Rect& clip); void reset(); bool isEnabled() { return mEnabled; } void dump(); diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp new file mode 100644 index 000000000000..db6402ce136f --- /dev/null +++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestSceneBase.h" + +class ClippingAnimation; + +static TestScene::Registrar _RectGrid(TestScene::Info{ + "clip", + "Complex clip cases" + "Low CPU/GPU load.", + TestScene::simpleCreateScene<ClippingAnimation> +}); + +class ClippingAnimation : public TestScene { +public: + sp<RenderNode> card; + void createContent(int width, int height, TestCanvas& canvas) override { + canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + card = TestUtils::createNode(0, 0, 200, 400, + [](RenderProperties& props, TestCanvas& canvas) { + canvas.save(SkCanvas::kMatrixClip_SaveFlag); + { + canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); + canvas.translate(100, 100); + canvas.rotate(45); + canvas.translate(-100, -100); + canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op); + canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode); + } + canvas.restore(); + + canvas.save(SkCanvas::kMatrixClip_SaveFlag); + { + SkPath clipCircle; + clipCircle.addCircle(100, 300, 100); + canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op); + canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode); + } + canvas.restore(); + + // put on a layer, to test stencil attachment + props.mutateLayerProperties().setType(LayerType::RenderLayer); + props.setAlpha(0.9f); + }); + canvas.drawRenderNode(card.get()); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 150; + card->mutateStagingProperties().setTranslationX(curFrame); + card->mutateStagingProperties().setTranslationY(curFrame); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } +}; diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index ac781243c25e..935ddcf9212d 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -22,6 +22,7 @@ #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" #include "tests/common/TestUtils.h" +#include "utils/Color.h" #include <functional> diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp index f9f5316a58df..3fd822d71310 100644 --- a/libs/hwui/tests/unit/BakedOpStateTests.cpp +++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp @@ -17,6 +17,7 @@ #include <gtest/gtest.h> #include <BakedOpState.h> +#include <ClipArea.h> #include <RecordedOp.h> #include <tests/common/TestUtils.h> @@ -24,31 +25,33 @@ namespace android { namespace uirenderer { TEST(ResolvedRenderState, construct) { + LinearAllocator allocator; Matrix4 translate10x20; translate10x20.loadTranslate(10, 20, 0); SkPaint paint; - RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(100, 200), &paint); + ClipRect clip(Rect(100, 200)); + RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, &clip, &paint); { // recorded with transform, no parent transform auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200)); - ResolvedRenderState state(*parentSnapshot, recordedOp, false); + ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false); EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20); - EXPECT_EQ(Rect(100, 200), state.clipRect); + EXPECT_EQ(Rect(100, 200), state.clipRect()); EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags); } { // recorded with transform and parent transform auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200)); - ResolvedRenderState state(*parentSnapshot, recordedOp, false); + ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false); Matrix4 expectedTranslate; expectedTranslate.loadTranslate(20, 40, 0); EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform); // intersection of parent & transformed child clip - EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect); + EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect()); // translated and also clipped EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds); @@ -57,22 +60,24 @@ TEST(ResolvedRenderState, construct) { } TEST(ResolvedRenderState, computeLocalSpaceClip) { + LinearAllocator allocator; Matrix4 translate10x20; translate10x20.loadTranslate(10, 20, 0); SkPaint paint; - RectOp recordedOp(Rect(1000, 1000), translate10x20, Rect(100, 200), &paint); + ClipRect clip(Rect(100, 200)); + RectOp recordedOp(Rect(1000, 1000), translate10x20, &clip, &paint); { // recorded with transform, no parent transform auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200)); - ResolvedRenderState state(*parentSnapshot, recordedOp, false); + ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false); EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip()) << "Local clip rect should be 100x200, offset by -10,-20"; } { // recorded with transform + parent transform auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200)); - ResolvedRenderState state(*parentSnapshot, recordedOp, false); + ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false); EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip()) << "Local clip rect should be 90x190, offset by -10,-20"; } @@ -149,6 +154,7 @@ const static StrokeTestCase sStrokeTestCases[] = { }; TEST(ResolvedRenderState, construct_expandForStroke) { + LinearAllocator allocator; // Loop over table of test cases and verify different combinations of stroke width and transform for (auto&& testCase : sStrokeTestCases) { SkPaint strokedPaint; @@ -156,14 +162,15 @@ TEST(ResolvedRenderState, construct_expandForStroke) { strokedPaint.setStyle(SkPaint::kStroke_Style); strokedPaint.setStrokeWidth(testCase.strokeWidth); + ClipRect clip(Rect(200, 200)); RectOp recordedOp(Rect(50, 50, 150, 150), - Matrix4::identity(), Rect(200, 200), &strokedPaint); + Matrix4::identity(), &clip, &strokedPaint); Matrix4 snapshotMatrix; snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1); auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200)); - ResolvedRenderState state(*parentSnapshot, recordedOp, true); + ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true); testCase.validator(state); } } @@ -175,8 +182,9 @@ TEST(BakedOpState, tryConstruct) { translate100x0.loadTranslate(100, 0, 0); SkPaint paint; + ClipRect clip(Rect(100, 200)); { - RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(100, 200), &paint); + RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200)); BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp); @@ -184,7 +192,7 @@ TEST(BakedOpState, tryConstruct) { EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op } { - RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(100, 200), &paint); + RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200)); BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp); @@ -218,7 +226,8 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { SkPaint paint; paint.setStyle(SkPaint::kStrokeAndFill_Style); paint.setStrokeWidth(0.0f); - RectOp rejectOp(Rect(100, 200), Matrix4::identity(), Rect(100, 200), &paint); + ClipRect clip(Rect(100, 200)); + RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined); @@ -231,7 +240,8 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { SkPaint paint; paint.setStyle(SkPaint::kStrokeAndFill_Style); paint.setStrokeWidth(10.0f); - RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint); + ClipRect clip(Rect(200, 200)); + RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200)); auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined); @@ -245,7 +255,8 @@ TEST(BakedOpState, tryStrokeableOpConstruct) { SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setStrokeWidth(10.0f); - RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint); + ClipRect clip(Rect(200, 200)); + RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint); auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200)); auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::Forced); diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp index c4d305e5de18..4cae737ab295 100644 --- a/libs/hwui/tests/unit/ClipAreaTests.cpp +++ b/libs/hwui/tests/unit/ClipAreaTests.cpp @@ -119,5 +119,122 @@ TEST(ClipArea, replaceNegative) { EXPECT_EQ(expected, area.getClipRect()); } +TEST(ClipArea, serializeClip) { + ClipArea area(createClipArea()); + LinearAllocator allocator; + + // unset clip + EXPECT_EQ(nullptr, area.serializeClip(allocator)); + + // rect clip + area.setClip(0, 0, 200, 200); + { + auto serializedClip = area.serializeClip(allocator); + ASSERT_NE(nullptr, serializedClip); + ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode); + auto clipRect = reinterpret_cast<const ClipRect*>(serializedClip); + ASSERT_EQ(Rect(200, 200), clipRect->rect); + EXPECT_EQ(serializedClip, area.serializeClip(allocator)) + << "Requery of clip on unmodified ClipArea must return same pointer."; + } + + // rect list + Matrix4 rotate; + rotate.loadRotate(2.0f); + area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); + { + auto serializedClip = area.serializeClip(allocator); + ASSERT_NE(nullptr, serializedClip); + ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode); + auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip); + ASSERT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); + EXPECT_EQ(serializedClip, area.serializeClip(allocator)) + << "Requery of clip on unmodified ClipArea must return same pointer."; + } + + // region + SkPath circlePath; + circlePath.addCircle(100, 100, 100); + area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); + { + auto serializedClip = area.serializeClip(allocator); + ASSERT_NE(nullptr, serializedClip); + ASSERT_EQ(ClipMode::Region, serializedClip->mode); + auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip); + ASSERT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds()) + << "Clip region should be 200x200"; + EXPECT_EQ(serializedClip, area.serializeClip(allocator)) + << "Requery of clip on unmodified ClipArea must return same pointer."; + } } + +TEST(ClipArea, serializeIntersectedClip) { + ClipArea area(createClipArea()); + LinearAllocator allocator; + + // simple state; + EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); + area.setClip(0, 0, 200, 200); + { + auto origRectClip = area.serializeClip(allocator); + ASSERT_NE(nullptr, origRectClip); + EXPECT_EQ(origRectClip, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity())); + } + + // rect + { + ClipRect recordedClip(Rect(100, 100)); + Matrix4 translateScale; + translateScale.loadTranslate(100, 100, 0); + translateScale.scale(2, 3, 1); + auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale); + ASSERT_NE(nullptr, resolvedClip); + ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode); + EXPECT_EQ(Rect(100, 100, 200, 200), + reinterpret_cast<const ClipRect*>(resolvedClip)->rect); + + EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale)) + << "Must return previous serialization, since input is same"; + + ClipRect recordedClip2(Rect(100, 100)); + EXPECT_NE(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip2, translateScale)) + << "Shouldn't return previous serialization, since matrix location is different"; + } + + // rect list + Matrix4 rotate; + rotate.loadRotate(2.0f); + area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op); + { + ClipRect recordedClip(Rect(100, 100)); + auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity()); + ASSERT_NE(nullptr, resolvedClip); + ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode); + auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip); + EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount()); + } + + // region + SkPath circlePath; + circlePath.addCircle(100, 100, 100); + area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op); + { + SkPath ovalPath; + ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200)); + + ClipRegion recordedClip; + recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200))); + + Matrix4 translate10x20; + translate10x20.loadTranslate(10, 20, 0); + auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, + translate10x20); // Note: only translate for now, others not handled correctly + ASSERT_NE(nullptr, resolvedClip); + ASSERT_EQ(ClipMode::Region, resolvedClip->mode); + auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip); + EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds()); + } } + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp index 78d65ddaa19d..5c442901045e 100644 --- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp +++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp @@ -27,7 +27,7 @@ struct SimplePair { int two = 2; }; -TEST(LinearAllocator, alloc) { +TEST(LinearAllocator, create) { LinearAllocator la; EXPECT_EQ(0u, la.usedSize()); la.alloc(64); @@ -35,7 +35,7 @@ TEST(LinearAllocator, alloc) { // so the usedSize isn't strictly defined EXPECT_LE(64u, la.usedSize()); EXPECT_GT(80u, la.usedSize()); - auto pair = la.alloc<SimplePair>(); + auto pair = la.create<SimplePair>(); EXPECT_LE(64u + sizeof(SimplePair), la.usedSize()); EXPECT_GT(80u + sizeof(SimplePair), la.usedSize()); EXPECT_EQ(1, pair->one); @@ -47,8 +47,8 @@ TEST(LinearAllocator, dtor) { { LinearAllocator la; for (int i = 0; i < 5; i++) { - la.alloc<TestUtils::SignalingDtor>()->setSignal(destroyed + i); - la.alloc<SimplePair>(); + la.create<TestUtils::SignalingDtor>()->setSignal(destroyed + i); + la.create<SimplePair>(); } la.alloc(100); for (int i = 0; i < 5; i++) { @@ -75,7 +75,7 @@ TEST(LinearAllocator, rewind) { la.rewindIfLastAlloc(addr, 100); EXPECT_GT(16u, la.usedSize()); size_t emptySize = la.usedSize(); - auto sigdtor = la.alloc<TestUtils::SignalingDtor>(); + auto sigdtor = la.create<TestUtils::SignalingDtor>(); sigdtor->setSignal(&destroyed); EXPECT_EQ(0, destroyed); EXPECT_LE(emptySize, la.usedSize()); diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp index b28e4361bc4a..0d1311899586 100644 --- a/libs/hwui/tests/unit/OpReordererTests.cpp +++ b/libs/hwui/tests/unit/OpReordererTests.cpp @@ -64,7 +64,7 @@ public: ADD_FAILURE() << "Layer updates not expected in this test"; } virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {} - virtual void endFrame() {} + virtual void endFrame(const Rect& repaintRect) {} // define virtual defaults for single draw methods #define X(Type) \ @@ -127,7 +127,7 @@ TEST(OpReorderer, simple) { void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { EXPECT_EQ(2, mIndex++); } - void endFrame() override { + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); } }; @@ -327,7 +327,7 @@ RENDERTHREAD_TEST(OpReorderer, textureLayer) { public: void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override { EXPECT_EQ(0, mIndex++); - EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect); + EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect()); EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds); Matrix4 expected; @@ -405,7 +405,7 @@ TEST(OpReorderer, clipped) { void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { EXPECT_EQ(0, mIndex++); EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds); - EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect); + EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect()); EXPECT_TRUE(state.computedState.transform.isIdentity()); } }; @@ -439,7 +439,7 @@ TEST(OpReorderer, saveLayerSimple) { EXPECT_EQ(1, mIndex++); EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds); EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds); - EXPECT_EQ(Rect(180, 180), state.computedState.clipRect); + EXPECT_EQ(Rect(180, 180), state.computedState.clipRect()); Matrix4 expectedTransform; expectedTransform.loadTranslate(-10, -10, 0); @@ -448,7 +448,7 @@ TEST(OpReorderer, saveLayerSimple) { void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(3, mIndex++); EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds); - EXPECT_EQ(Rect(200, 200), state.computedState.clipRect); + EXPECT_EQ(Rect(200, 200), state.computedState.clipRect()); EXPECT_TRUE(state.computedState.transform.isIdentity()); } }; @@ -494,7 +494,7 @@ TEST(OpReorderer, saveLayerNested) { void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(7, mIndex++); } - void endFrame() override { + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(9, mIndex++); } void onRectOp(const RectOp& op, const BakedOpState& state) override { @@ -574,7 +574,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) { EXPECT_TRUE(state.computedState.transform.isIdentity()) << "Transform should be reset within layer"; - EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75)) + EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) << "Damage rect should be used to clip layer content"; } void endLayer() override { @@ -586,7 +586,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) { void onLayerOp(const LayerOp& op, const BakedOpState& state) override { EXPECT_EQ(4, mIndex++); } - void endFrame() override { + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(5, mIndex++); } }; @@ -675,7 +675,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayerComplex) { EXPECT_EQ(200u, layer->viewportHeight); } else { ADD_FAILURE(); } } - void endFrame() override { + void endFrame(const Rect& repaintRect) override { EXPECT_EQ(12, mIndex++); } }; diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 08f927ce00fd..a63cb18d7c4b 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -33,6 +33,14 @@ static void playbackOps(const DisplayList& displayList, } } +#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ + EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ + if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ + EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ + } else { \ + ADD_FAILURE() << "ClipState not a rect"; \ + } + TEST(RecordingCanvas, emptyPlayback) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -41,6 +49,22 @@ TEST(RecordingCanvas, emptyPlayback) { playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); }); } +TEST(RecordingCanvas, clipRect) { + auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) { + canvas.save(SkCanvas::kMatrixClip_SaveFlag); + canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op); + canvas.drawRect(0, 0, 50, 50, SkPaint()); + canvas.drawRect(50, 50, 100, 100, SkPaint()); + canvas.restore(); + }); + + ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops"; + EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip); + EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip); + EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip) + << "Clip should be serialized once"; +} + TEST(RecordingCanvas, drawLines) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { SkPaint paint; @@ -66,7 +90,7 @@ TEST(RecordingCanvas, drawRect) { ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; auto op = *(dl->getOps()[0]); ASSERT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_EQ(Rect(100, 200), op.localClipRect); + EXPECT_EQ(nullptr, op.localClip); EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds); } @@ -83,7 +107,7 @@ TEST(RecordingCanvas, drawText) { playbackOps(*dl, [&count](const RecordedOp& op) { count++; ASSERT_EQ(RecordedOpId::TextOp, op.opId); - EXPECT_EQ(Rect(200, 200), op.localClipRect); + EXPECT_EQ(nullptr, op.localClip); EXPECT_TRUE(op.localMatrix.isIdentity()); EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25)) << "Op expected to be 25+ pixels wide, 10+ pixels tall"; @@ -185,7 +209,7 @@ TEST(RecordingCanvas, backgroundAndImage) { ASSERT_NE(nullptr, op.paint); EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); EXPECT_EQ(Rect(100, 200), op.unmappedBounds); - EXPECT_EQ(Rect(100, 200), op.localClipRect); + EXPECT_EQ(nullptr, op.localClip); Matrix4 expectedMatrix; expectedMatrix.loadIdentity(); @@ -194,7 +218,7 @@ TEST(RecordingCanvas, backgroundAndImage) { ASSERT_EQ(RecordedOpId::BitmapOp, op.opId); EXPECT_EQ(nullptr, op.paint); EXPECT_EQ(Rect(25, 25), op.unmappedBounds); - EXPECT_EQ(Rect(100, 200), op.localClipRect); + EXPECT_EQ(nullptr, op.localClip); Matrix4 expectedMatrix; expectedMatrix.loadTranslate(25, 25, 0); @@ -219,12 +243,12 @@ TEST(RecordingCanvas, saveLayer_simple) { case 0: EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId); EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); - EXPECT_EQ(Rect(200, 200), op.localClipRect); + EXPECT_EQ(nullptr, op.localClip); EXPECT_TRUE(op.localMatrix.isIdentity()); break; case 1: EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_EQ(Rect(180, 160), op.localClipRect); + EXPECT_CLIP_RECT(Rect(180, 160), op.localClip); EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds); expectedMatrix.loadTranslate(-10, -20, 0); EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); @@ -254,8 +278,8 @@ TEST(RecordingCanvas, saveLayer_viewportCrop) { if (count++ == 1) { Matrix4 expectedMatrix; EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_EQ(Rect(100, 100), op.localClipRect) << "Recorded clip rect should be" - " intersection of viewport and saveLayer bounds, in layer space"; + EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be + // intersection of viewport and saveLayer bounds, in layer space; EXPECT_EQ(Rect(400, 400), op.unmappedBounds); expectedMatrix.loadTranslate(-100, -100, 0); EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); @@ -281,7 +305,7 @@ TEST(RecordingCanvas, saveLayer_rotateUnclipped) { playbackOps(*dl, [&count](const RecordedOp& op) { if (count++ == 1) { EXPECT_EQ(RecordedOpId::RectOp, op.opId); - EXPECT_EQ(Rect(100, 100), op.localClipRect); + EXPECT_CLIP_RECT(Rect(100, 100), op.localClip); EXPECT_EQ(Rect(100, 100), op.unmappedBounds); EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix) << "Recorded op shouldn't see any canvas transform before the saveLayer"; @@ -312,7 +336,16 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) { // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by // the parent 200x200 viewport, but prior to rotation - EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), op.localClipRect); + ASSERT_NE(nullptr, op.localClip); + ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode); + // NOTE: this check relies on saveLayer altering the clip post-viewport init. This + // causes the clip to be recorded by contained draw commands, though it's not necessary + // since the same clip will be computed at draw time. If such a change is made, this + // check could be done at record time by querying the clip, or the clip could be altered + // slightly so that it is serialized. + EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), + (reinterpret_cast<const ClipRect*>(op.localClip))->rect); + EXPECT_EQ(Rect(400, 400), op.unmappedBounds); expectedMatrix.loadIdentity(); EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix); diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index e1c6f6c70428..dcbc0dda951a 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -56,12 +56,12 @@ public: void* alloc(size_t size); /** - * Allocates an instance of the template type with the default constructor + * Allocates an instance of the template type with the given construction parameters * and adds it to the automatic destruction list. */ - template<class T> - T* alloc() { - T* ret = new (*this) T; + template<class T, typename... Params> + T* create(Params... params) { + T* ret = new (*this) T(params...); autoDestroy(ret); return ret; } diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png Binary files differdeleted file mode 100644 index 65e42aa4a196..000000000000 --- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png Binary files differdeleted file mode 100644 index 1de8292c0721..000000000000 --- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png Binary files differdeleted file mode 100644 index 00b8a8b719d8..000000000000 --- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png Binary files differdeleted file mode 100644 index 52367749f8d4..000000000000 --- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png Binary files differdeleted file mode 100644 index c7daa2a6fcaf..000000000000 --- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png +++ /dev/null diff --git a/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml b/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml deleted file mode 100644 index 27cfa81dd847..000000000000 --- a/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_root_sdcard_alpha" - android:tint="?android:attr/colorControlNormal" /> diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml index 84c0343185ac..3021b16371ad 100644 --- a/packages/SystemUI/res/drawable/docked_divider_handle.xml +++ b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml @@ -1,7 +1,7 @@ <!-- Copyright (C) 2015 The Android Open Source Project - Licensed under the Apache License, Version 2 (the "License"); + 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 @@ -13,10 +13,12 @@ Copyright (C) 2015 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <size android:height="@dimen/docked_divider_handle_height" - android:width="@dimen/docked_divider_handle_width" /> - <corners android:radius="1dp" /> - <solid android:color="@color/docked_divider_handle" /> -</shape>
\ No newline at end of file +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M17 1.01L7 1c-1.1 0,-2 .9,-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2,-.9 2,-2V3c0,-1.1,-.9,-1.99,-2,-1.99zM17 19H7V5h10v14z"/> +</vector> diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml index b522e0a00916..c303e0ca04a7 100644 --- a/packages/DocumentsUI/res/values-sw/strings.xml +++ b/packages/DocumentsUI/res/values-sw/strings.xml @@ -97,8 +97,8 @@ <string name="copy_failure_alert_content" msgid="3715575000297709082">"Faili hizi hazikunakiliwa: <xliff:g id="LIST">%1$s</xliff:g>"</string> <string name="move_failure_alert_content" msgid="7151140279020481180">"Faili hizi hazikuhamishwa: <xliff:g id="LIST">%1$s</xliff:g>"</string> <plurals name="clipboard_files_clipped" formatted="false" msgid="855459017537058539"> - <item quantity="other">Alinakili faili <xliff:g id="COUNT_1">%1$d</xliff:g> kwenye ubaoklipu.</item> - <item quantity="one">Alinakili faili <xliff:g id="COUNT_0">%1$d</xliff:g> kwenye ubaoklipu.</item> + <item quantity="other">Alinakili faili <xliff:g id="COUNT_1">%1$d</xliff:g> kwenye ubao wa kunakili.</item> + <item quantity="one">Alinakili faili <xliff:g id="COUNT_0">%1$d</xliff:g> kwenye ubao wa kunakili.</item> </plurals> <string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Haiwezi kubandika faili zilizochaguliwa katika eneo hili."</string> </resources> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index 4eacee5bdaa9..6719b238326a 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -114,11 +114,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { if (userMode != State.MODE_UNKNOWN) { result.mode = userMode; } else { - if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) { - result.mode = State.MODE_GRID; - } else { - result.mode = State.MODE_LIST; - } + result.mode = State.MODE_GRID; } if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java index c81d4fbe0fb0..46372c00f872 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/State.java +++ b/packages/DocumentsUI/src/com/android/documentsui/State.java @@ -36,7 +36,7 @@ public class State implements android.os.Parcelable { /** Explicit user choice */ public int userMode = MODE_UNKNOWN; /** Derived after loader */ - public int derivedMode = MODE_LIST; + public int derivedMode = MODE_GRID; /** Explicit user choice */ public int userSortOrder = SORT_ORDER_UNKNOWN; diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 0d5e34e87ebb..9617582a1f3b 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -53,6 +53,7 @@ import android.provider.DocumentsContract.Document; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.GridLayoutManager.SpanSizeLookup; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.LayoutManager; @@ -99,18 +100,17 @@ import com.android.documentsui.dirlist.MultiSelectManager.Selection; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; + import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; /** * Display the documents inside a single directory. */ -public class DirectoryFragment extends Fragment { +public class DirectoryFragment extends Fragment implements DocumentsAdapter.Environment { public static final int TYPE_NORMAL = 1; public static final int TYPE_SEARCH = 2; @@ -126,7 +126,7 @@ public class DirectoryFragment extends Fragment { private static final String TAG = "DirectoryFragment"; private static final int LOADER_ID = 42; - private static final boolean DEBUG_ENABLE_DND = true; + static final boolean DEBUG_ENABLE_DND = true; private static final String EXTRA_TYPE = "type"; private static final String EXTRA_ROOT = "root"; @@ -289,7 +289,11 @@ public class DirectoryFragment extends Fragment { final RootInfo root = getArguments().getParcelable(EXTRA_ROOT); final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC); - mAdapter = new DocumentsAdapter(); + mIconHelper = new IconHelper(context, state.derivedMode); + + mAdapter = new SectionBreakDocumentsAdapterWrapper( + this, new ModelBackedDocumentsAdapter(this, mIconHelper)); + mRecView.setAdapter(mAdapter); GestureDetector.SimpleOnGestureListener listener = @@ -328,12 +332,13 @@ public class DirectoryFragment extends Fragment { // into the selection manager. mSelectionManager = new MultiSelectManager( mRecView, + mAdapter, state.allowMultiple ? MultiSelectManager.MODE_MULTIPLE : MultiSelectManager.MODE_SINGLE); mSelectionManager.addCallback(new SelectionModeListener()); - mModel = new Model(context, mAdapter); + mModel = new Model(context); mModel.addUpdateListener(mAdapter); mModel.addUpdateListener(mModelUpdateListener); @@ -343,8 +348,6 @@ public class DirectoryFragment extends Fragment { mTuner = FragmentTuner.pick(state); mClipper = new DocumentClipper(context); - mIconHelper = new IconHelper(context, state.derivedMode); - boolean hideGridTitles; if (mType == TYPE_RECENT_OPEN) { // Hide titles when showing recents for picking images/videos @@ -574,7 +577,10 @@ public class DirectoryFragment extends Fragment { case MODE_GRID: if (mGridLayout == null) { mGridLayout = new GridLayoutManager(getContext(), mColumnCount); - mGridLayout.setSpanSizeLookup(mAdapter.createSpanSizeLookup()); + SpanSizeLookup lookup = mAdapter.createSpanSizeLookup(); + if (lookup != null) { + mGridLayout.setSpanSizeLookup(lookup); + } } layout = mGridLayout; break; @@ -609,6 +615,11 @@ public class DirectoryFragment extends Fragment { return columnCount; } + @Override + public int getColumnCount() { + return mColumnCount; + } + /** * Manages the integration between our ActionMode and MultiSelectManager, initiating * ActionMode when there is a selection, canceling it when there is no selection, @@ -893,10 +904,34 @@ public class DirectoryFragment extends Fragment { }.execute(selected); } - private State getDisplayState() { + @Override + public void initDocumentHolder(DocumentHolder holder) { + holder.addClickListener(mItemClickListener); + holder.addOnKeyListener(mSelectionManager); + } + + @Override + public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) { + if (DEBUG_ENABLE_DND) { + setupDragAndDropOnDocumentView(holder.itemView, cursor); + } + } + + @Override + public State getDisplayState() { return ((BaseActivity) getActivity()).getDisplayState(); } + @Override + public Model getModel() { + return mModel; + } + + @Override + public boolean isDocumentEnabled(String docMimeType, int docFlags) { + return mTuner.isDocumentEnabled(docMimeType, docFlags); + } + void showEmptyView() { mEmptyView.setVisibility(View.VISIBLE); mRecView.setVisibility(View.GONE); @@ -920,240 +955,6 @@ public class DirectoryFragment extends Fragment { mRecView.setVisibility(View.VISIBLE); } - final class DocumentsAdapter - extends RecyclerView.Adapter<DocumentHolder> - implements Model.UpdateListener { - - private static final String TAG = "DocumentsAdapter"; - public static final int ITEM_TYPE_LAYOUT_DIVIDER = 0; - public static final int ITEM_TYPE_DOCUMENT = 1; - public static final int ITEM_TYPE_DIRECTORY = 2; - - /** - * An ordered list of model IDs. This is the data structure that determines what shows up in - * the UI, and where. - */ - private List<String> mModelIds = new ArrayList<>(); - - // The list is divided into two segments - directories, and everything else. Record the - // position where the transition happens. - private int mDividerPosition; - - public GridLayoutManager.SpanSizeLookup createSpanSizeLookup() { - return new GridLayoutManager.SpanSizeLookup() { - @Override - public int getSpanSize(int position) { - // Make layout whitespace span the grid. This has the effect of breaking - // grid rows whenever layout whitespace is encountered. - if (getItemViewType(position) == ITEM_TYPE_LAYOUT_DIVIDER) { - return mColumnCount; - } else { - return 1; - } - } - }; - } - - @Override - public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == ITEM_TYPE_LAYOUT_DIVIDER) { - return new EmptyDocumentHolder(getContext()); - }; - - DocumentHolder holder = null; - final State state = getDisplayState(); - switch (state.derivedMode) { - case MODE_GRID: - switch (viewType) { - case ITEM_TYPE_DIRECTORY: - holder = new GridDirectoryHolder(getContext(), parent); - break; - case ITEM_TYPE_DOCUMENT: - holder = new GridDocumentHolder(getContext(), parent, mIconHelper); - break; - default: - throw new IllegalStateException("Unsupported layout type."); - } - break; - case MODE_LIST: - holder = new ListDocumentHolder(getContext(), parent, mIconHelper); - break; - case MODE_UNKNOWN: - default: - throw new IllegalStateException("Unsupported layout mode."); - } - - holder.addClickListener(mItemClickListener); - holder.addOnKeyListener(mSelectionManager); - return holder; - } - - /** - * Deal with selection changed events by using a custom ItemAnimator that just changes the - * background color. This works around focus issues (otherwise items lose focus when their - * selection state changes) but also optimizes change animations for selection. - */ - @Override - public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) { - if (holder.getItemViewType() == ITEM_TYPE_LAYOUT_DIVIDER) { - // Whitespace items are hidden elements with no data to bind. - return; - } - - final View itemView = holder.itemView; - - if (payload.contains(MultiSelectManager.SELECTION_CHANGED_MARKER)) { - final boolean selected = isSelected(mModelIds.get(position)); - itemView.setActivated(selected); - return; - } else { - onBindViewHolder(holder, position); - } - } - - @Override - public void onBindViewHolder(DocumentHolder holder, int position) { - if (holder.getItemViewType() == ITEM_TYPE_LAYOUT_DIVIDER) { - // Whitespace items are hidden elements with no data to bind. - return; - } - - String modelId = mModelIds.get(position); - Cursor cursor = mModel.getItem(modelId); - holder.bind(cursor, modelId, getDisplayState()); - - final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); - final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); - - holder.setSelected(isSelected(modelId)); - holder.setEnabled(mTuner.isDocumentEnabled(docMimeType, docFlags)); - if (DEBUG_ENABLE_DND) { - setupDragAndDropOnDocumentView(holder.itemView, cursor); - } - } - - @Override - public int getItemCount() { - return mModelIds.size(); - } - - @Override - public void onModelUpdate(Model model) { - mModelIds = Lists.newArrayList(model.getModelIds()); - // Start the divider at the end. That way if the code below encounters no documents - // (i.e. in a directory containing only directories), the divider is placed at the end - // of the list, as expected. - mDividerPosition = mModelIds.size(); - - // Walk down the list of IDs till we encounter something that's not a directory, and - // insert a whitespace element - this introduces a visual break in the grid between - // folders and documents. - // TODO: This code makes assumptions about the model, namely, that it performs a - // bucketed sort where directories will always be ordered before other files. CBB. - for (int i = 0; i < mModelIds.size(); ++i) { - final String mimeType = getCursorString( - model.getItem(mModelIds.get(i)), Document.COLUMN_MIME_TYPE); - if (!Document.MIME_TYPE_DIR.equals(mimeType)) { - mDividerPosition = i; - break; - } - } - - mModelIds.add(mDividerPosition, null); - } - - @Override - public void onModelUpdateFailed(Exception e) { - if (DEBUG) Log.d(TAG, "onModelUpdateFailed called "); - mModelIds.clear(); - } - - /** - * @return The model ID of the item at the given adapter position. - */ - public String getModelId(int adapterPosition) { - return mModelIds.get(adapterPosition); - } - - /** - * Hides a set of items from the associated RecyclerView. - * - * @param ids The Model IDs of the items to hide. - * @return A SparseArray that maps the hidden IDs to their old positions. This can be used - * to {@link #unhide} the items if necessary. - */ - public SparseArray<String> hide(String... ids) { - Set<String> toHide = Sets.newHashSet(ids); - - // Proceed backwards through the list of items, because each removal causes the - // positions of all subsequent items to change. - SparseArray<String> hiddenItems = new SparseArray<>(); - for (int i = mModelIds.size() - 1; i >= 0; --i) { - String id = mModelIds.get(i); - if (toHide.contains(id)) { - hiddenItems.put(i, mModelIds.remove(i)); - notifyItemRemoved(i); - } - } - - return hiddenItems; - } - - /** - * Unhides a set of previously hidden items. - * - * @param ids A sparse array of IDs from a previous call to {@link #hide}. - */ - public void unhide(SparseArray<String> ids) { - // Proceed backwards through the list of items, because each addition causes the - // positions of all subsequent items to change. - for (int i = ids.size() - 1; i >= 0; --i) { - int pos = ids.keyAt(i); - String id = ids.get(pos); - mModelIds.add(pos, id); - notifyItemInserted(pos); - } - } - - /** - * Returns a list of model IDs of items currently in the adapter. Excludes items that are - * currently hidden (see {@link #hide(String...)}). - * - * @return A list of Model IDs. - */ - public List<String> getModelIds() { - return mModelIds; - } - - @Override - public int getItemViewType(int position) { - if (position < mDividerPosition) { - return ITEM_TYPE_DIRECTORY; - } else if (position == mDividerPosition) { - return ITEM_TYPE_LAYOUT_DIVIDER; - } else { - return ITEM_TYPE_DOCUMENT; - } - } - - /** - * Triggers item-change notifications by stable ID. Passing an unrecognized ID will result - * in a warning in logcat, but no other error. - * - * @param id - * @param selectionChangedMarker - */ - public void notifyItemChanged(String id, String selectionChangedMarker) { - int position = mModelIds.indexOf(id); - - if (position >= 0) { - notifyItemChanged(position, selectionChangedMarker); - } else { - Log.w(TAG, "Item change notification received for unknown item: " + id); - } - } - } - private String findCommonMimeType(List<String> mimeTypes) { String[] commonType = mimeTypes.get(0).split("/"); if (commonType.length != 2) { @@ -1504,7 +1305,8 @@ public class DirectoryFragment extends Fragment { abstract void onDocumentsReady(List<DocumentInfo> docs); } - boolean isSelected(String modelId) { + @Override + public boolean isSelected(String modelId) { return mSelectionManager.getSelection().contains(modelId); } @@ -1520,7 +1322,7 @@ public class DirectoryFragment extends Fragment { } } - private class ModelUpdateListener implements Model.UpdateListener { + private final class ModelUpdateListener implements Model.UpdateListener { @Override public void onModelUpdate(Model model) { if (model.info != null || model.error != null) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java new file mode 100644 index 000000000000..43c2f6386347 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import static com.android.documentsui.model.DocumentInfo.getCursorString; + +import android.content.Context; +import android.database.Cursor; +import android.provider.DocumentsContract.Document; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.SparseArray; + +import com.android.documentsui.State; + +import java.nio.channels.UnsupportedAddressTypeException; +import java.util.List; + +/** + * DocumentsAdapter provides glue between a directory Model, and RecylcerView. We've + * abstracted this a bit in order to decompose some specialized support + * for adding dummy layout objects (@see SectionBreakDocumentsAdapter). Handling of the + * dummy layout objects was error prone when interspersed with the core mode / adapter code. + * + * @see ModelBackedDocumentsAdapter + * @see SectionBreakDocumentsAdapter + */ +abstract class DocumentsAdapter + extends RecyclerView.Adapter<DocumentHolder> + implements Model.UpdateListener { + + // Payloads for notifyItemChange to distinguish between selection and other events. + static final String SELECTION_CHANGED_MARKER = "Selection-Changed"; + + /** + * Returns a list of model IDs of items currently in the adapter. Excludes items that are + * currently hidden (see {@link #hide(String...)}). + * + * @return A list of Model IDs. + */ + abstract List<String> getModelIds(); + + /** + * Triggers item-change notifications by stable ID (as opposed to position). + * Passing an unrecognized ID will result in a warning in logcat, but no other error. + */ + abstract void onItemSelectionChanged(String id); + + /** + * @return The model ID of the item at the given adapter position. + */ + abstract String getModelId(int position); + + /** + * Hides a set of items from the associated RecyclerView. + * + * @param ids The Model IDs of the items to hide. + * @return A SparseArray that maps the hidden IDs to their old positions. This can be used + * to {@link #unhide} the items if necessary. + */ + abstract public SparseArray<String> hide(String... ids); + + /** + * Unhides a set of previously hidden items. + * + * @param ids A sparse array of IDs from a previous call to {@link #hide}. + */ + abstract void unhide(SparseArray<String> ids); + + /** + * Returns a class that yields the span size for a particular element. This is + * primarily useful in {@link SectionBreakDocumentsAdapterWrapper} where + * we adjust sizes. + */ + GridLayoutManager.SpanSizeLookup createSpanSizeLookup() { + throw new UnsupportedAddressTypeException(); + } + + static boolean isDirectory(Cursor cursor) { + final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); + return Document.MIME_TYPE_DIR.equals(mimeType); + } + + boolean isDirectory(Model model, int position) { + String modelId = getModelIds().get(position); + Cursor cursor = model.getItem(modelId); + return isDirectory(cursor); + } + + /** + * Environmental access for View adapter implementations. + */ + interface Environment { + Context getContext(); + int getColumnCount(); + State getDisplayState(); + boolean isSelected(String id); + Model getModel(); + boolean isDocumentEnabled(String mimeType, int flags); + void initDocumentHolder(DocumentHolder holder); + void onBindDocumentHolder(DocumentHolder holder, Cursor cursor); + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java index bea38c64ae68..f2bade588f23 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java @@ -35,7 +35,6 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; -import android.support.v7.widget.RecyclerView; import android.util.Log; import com.android.documentsui.BaseActivity.SiblingProvider; @@ -74,7 +73,7 @@ public class Model implements SiblingProvider { @Nullable String info; @Nullable String error; - Model(Context context, RecyclerView.Adapter<?> viewAdapter) { + Model(Context context) { mContext = context; } @@ -87,8 +86,19 @@ public class Model implements SiblingProvider { private static String createModelId(Cursor c) { // TODO: Maybe more efficient to use just the document ID, in cases where there is only one // authority (which should be the majority of cases). - return getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY) + - "|" + getCursorString(c, Document.COLUMN_DOCUMENT_ID); + return createModelId( + getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY), + getCursorString(c, Document.COLUMN_DOCUMENT_ID)); + } + + /** + * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique + * string that can be used to identify the document referred to by the cursor. + * + * @param c A cursor that refers to a document. + */ + static String createModelId(String authority, String docId) { + return authority + "|" + docId; } private void notifyUpdateListeners() { @@ -404,8 +414,14 @@ public class Model implements SiblingProvider { @Override protected Void doInBackground(Selection... selected) { - List<DocumentInfo> toDelete = getDocuments(selected[0]); - mHadTrouble = false; + List<DocumentInfo> toDelete = null; + try { + toDelete = getDocuments(selected[0]); + } catch (NullPointerException e) { + Log.w(TAG, "Failed to retrieve documents for delete."); + mHadTrouble = true; + return null; + } for (DocumentInfo doc : toDelete) { if (!doc.isDeleteSupported()) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java new file mode 100644 index 000000000000..68756a36fb1c --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import static com.android.documentsui.Shared.DEBUG; +import static com.android.documentsui.State.MODE_GRID; +import static com.android.documentsui.State.MODE_LIST; +import static com.android.documentsui.State.MODE_UNKNOWN; +import static com.android.documentsui.model.DocumentInfo.getCursorInt; +import static com.android.documentsui.model.DocumentInfo.getCursorString; + +import android.database.Cursor; +import android.provider.DocumentsContract.Document; +import android.util.Log; +import android.util.SparseArray; +import android.view.ViewGroup; + +import com.android.documentsui.State; + +import com.google.common.collect.Sets; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Adapts from dirlist.Model to something RecyclerView understands. + */ +final class ModelBackedDocumentsAdapter extends DocumentsAdapter { + + private static final String TAG = "ModelBackedDocumentsAdapter"; + public static final int ITEM_TYPE_DOCUMENT = 1; + public static final int ITEM_TYPE_DIRECTORY = 2; + + // Provides access to information needed when creating and view holders. This + // isn't an ideal pattern (more transitive dependency stuff) but good enough for now. + private final Environment mEnv; + private final IconHelper mIconHelper; // a transitive dependency of the holders. + + /** + * An ordered list of model IDs. This is the data structure that determines what shows up in + * the UI, and where. + */ + private List<String> mModelIds = new ArrayList<>(); + + // List of files that have been deleted. Some transient directory updates + // may happen while files are being deleted. During this time we don't + // want once-hidden files to be re-shown. We only remove + // items from this list when we get a model update where the model + // does not contain a corresponding id. This ensures hidden entries + // don't momentarily re-appear if we get intermediate updates from + // the file system. + private Set<String> mHiddenIds = new HashSet<>(); + + public ModelBackedDocumentsAdapter(Environment env, IconHelper iconHelper) { + mEnv = env; + mIconHelper = iconHelper; + } + + @Override + public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) { + DocumentHolder holder = null; + final State state = mEnv.getDisplayState(); + switch (state.derivedMode) { + case MODE_GRID: + switch (viewType) { + case ITEM_TYPE_DIRECTORY: + holder = new GridDirectoryHolder(mEnv.getContext(), parent); + break; + case ITEM_TYPE_DOCUMENT: + holder = new GridDocumentHolder(mEnv.getContext(), parent, mIconHelper); + break; + default: + throw new IllegalStateException("Unsupported layout type."); + } + break; + case MODE_LIST: + holder = new ListDocumentHolder(mEnv.getContext(), parent, mIconHelper); + break; + case MODE_UNKNOWN: + default: + throw new IllegalStateException("Unsupported layout mode."); + } + + mEnv.initDocumentHolder(holder); + return holder; + } + + @Override + public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) { + if (payload.contains(SELECTION_CHANGED_MARKER)) { + final boolean selected = mEnv.isSelected(mModelIds.get(position)); + holder.setSelected(selected); + } else { + onBindViewHolder(holder, position); + } + } + + @Override + public void onBindViewHolder(DocumentHolder holder, int position) { + String modelId = mModelIds.get(position); + Cursor cursor = mEnv.getModel().getItem(modelId); + holder.bind(cursor, modelId, mEnv.getDisplayState()); + + final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); + final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); + + holder.setSelected(mEnv.isSelected(modelId)); + holder.setEnabled(mEnv.isDocumentEnabled(docMimeType, docFlags)); + + mEnv.onBindDocumentHolder(holder, cursor); + } + + @Override + public int getItemCount() { + return mModelIds.size(); + } + + @Override + public void onModelUpdate(Model model) { + if (DEBUG && mHiddenIds.size() > 0) { + Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds); + } + + List<String> modelIds = model.getModelIds(); + mModelIds = new ArrayList<>(modelIds.size()); + for (String id : modelIds) { + if (!mHiddenIds.contains(id)) { + mModelIds.add(id); + } else { + if (DEBUG) Log.d(TAG, "Omitting hidden id from model during update: " + id); + } + } + + // Finally remove any hidden ids that aren't present in the model. + // This assumes that model updates represent a complete set of files. + mHiddenIds.retainAll(mModelIds); + } + + @Override + public void onModelUpdateFailed(Exception e) { + Log.w(TAG, "Model update failed.", e); + mModelIds.clear(); + } + + @Override + public String getModelId(int adapterPosition) { + return mModelIds.get(adapterPosition); + } + + @Override + public SparseArray<String> hide(String... ids) { + if (DEBUG) Log.d(TAG, "Hiding ids: " + ids); + Set<String> toHide = Sets.newHashSet(ids); + + // Proceed backwards through the list of items, because each removal causes the + // positions of all subsequent items to change. + SparseArray<String> hiddenItems = new SparseArray<>(); + for (int i = mModelIds.size() - 1; i >= 0; --i) { + String id = mModelIds.get(i); + if (toHide.contains(id)) { + mHiddenIds.add(id); + hiddenItems.put(i, mModelIds.remove(i)); + notifyItemRemoved(i); + } + } + + return hiddenItems; + } + + @Override + public void unhide(SparseArray<String> ids) { + if (DEBUG) Log.d(TAG, "Un-iding ids: " + ids); + + // An ArrayList can shrink at runtime...and in fact + // it does when we clear it completely. + // This means we can't call add(pos, id) without + // first checking the list size. + List<String> oldIds = mModelIds; + mModelIds = new ArrayList<>(oldIds.size() + ids.size()); + mModelIds.addAll(oldIds); + + // Finally insert the unhidden items. + for (int i = 0; i < ids.size(); i++) { + int pos = ids.keyAt(i); + String id = ids.get(pos); + mHiddenIds.remove(id); + mModelIds.add(pos, id); + notifyItemInserted(pos); + } + } + + @Override + public List<String> getModelIds() { + return mModelIds; + } + + @Override + public int getItemViewType(int position) { + return isDirectory(mEnv.getModel(), position) + ? ITEM_TYPE_DIRECTORY + : ITEM_TYPE_DOCUMENT; + } + + @Override + public void onItemSelectionChanged(String id) { + int position = mModelIds.indexOf(id); + + if (position >= 0) { + notifyItemChanged(position, SELECTION_CHANGED_MARKER); + } else { + Log.w(TAG, "Item change notification received for unknown item: " + id); + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java index e47af6710c9a..5994df952de9 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java @@ -68,24 +68,23 @@ public final class MultiSelectManager implements View.OnKeyListener { private final Selection mSelection = new Selection(); - private Range mRanger; - private SelectionEnvironment mEnvironment; - + private final SelectionEnvironment mEnvironment; + private final DocumentsAdapter mAdapter; private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1); + private Range mRanger; private boolean mSingleSelect; - // Payloads for notifyItemChange to distinguish between selection and other events. - public static final String SELECTION_CHANGED_MARKER = "Selection-Changed"; - @Nullable private BandController mBandManager; + /** * @param recyclerView * @param mode Selection mode */ - public MultiSelectManager(final RecyclerView recyclerView, int mode) { - this(new RuntimeSelectionEnvironment(recyclerView), mode); + public MultiSelectManager( + final RecyclerView recyclerView, DocumentsAdapter adapter, int mode) { + this(new RuntimeSelectionEnvironment(recyclerView), adapter, mode); if (mode == MODE_MULTIPLE) { mBandManager = new BandController(); @@ -136,11 +135,12 @@ public final class MultiSelectManager implements View.OnKeyListener { * @hide */ @VisibleForTesting - MultiSelectManager(SelectionEnvironment environment, int mode) { + MultiSelectManager(SelectionEnvironment environment, DocumentsAdapter adapter, int mode) { mEnvironment = checkNotNull(environment, "'environment' cannot be null."); + mAdapter = checkNotNull(adapter, "'adapter' cannot be null."); mSingleSelect = mode == MODE_SINGLE; - mEnvironment.registerDataObserver( + mAdapter.registerAdapterDataObserver( new RecyclerView.AdapterDataObserver() { private List<String> mModelIds; @@ -149,7 +149,7 @@ public final class MultiSelectManager implements View.OnKeyListener { public void onChanged() { // TODO: This is causing b/22765812 mSelection.clear(); - mModelIds = mEnvironment.getModelIds(); + mModelIds = mAdapter.getModelIds(); } @Override @@ -339,7 +339,10 @@ public final class MultiSelectManager implements View.OnKeyListener { if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position."); return; } - toggleSelection(mEnvironment.getModelIdFromAdapterPosition(position)); + String id = mAdapter.getModelId(position); + if (id != null) { + toggleSelection(id); + } } /** @@ -348,6 +351,7 @@ public final class MultiSelectManager implements View.OnKeyListener { * @param modelId */ public void toggleSelection(String modelId) { + checkNotNull(modelId); boolean changed = false; if (mSelection.contains(modelId)) { changed = attemptDeselect(modelId); @@ -387,7 +391,7 @@ public final class MultiSelectManager implements View.OnKeyListener { return; } - if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(position))) { + if (mSelection.contains(mAdapter.getModelId(position))) { mRanger = new Range(position); } } @@ -404,7 +408,11 @@ public final class MultiSelectManager implements View.OnKeyListener { private void updateRange(int begin, int end, boolean selected) { checkState(end >= begin); for (int i = begin; i <= end; i++) { - String id = mEnvironment.getModelIdFromAdapterPosition(i); + String id = mAdapter.getModelId(i); + if (id == null) { + continue; + } + if (selected) { boolean canSelect = notifyBeforeItemStateChange(id, true); if (canSelect) { @@ -436,6 +444,7 @@ public final class MultiSelectManager implements View.OnKeyListener { * @return True if the update was applied. */ private boolean attemptDeselect(String id) { + checkArgument(id != null); if (notifyBeforeItemStateChange(id, false)) { mSelection.remove(id); notifyItemStateChanged(id, false); @@ -462,11 +471,12 @@ public final class MultiSelectManager implements View.OnKeyListener { * (identified by {@code position}) changes. */ private void notifyItemStateChanged(String id, boolean selected) { + checkArgument(id != null); int lastListener = mCallbacks.size() - 1; for (int i = lastListener; i > -1; i--) { mCallbacks.get(i).onItemStateChanged(id, selected); } - mEnvironment.notifyItemChanged(id); + mAdapter.onItemSelectionChanged(id); } /** @@ -613,7 +623,7 @@ public final class MultiSelectManager implements View.OnKeyListener { * @param id * @return true if the position is currently selected. */ - public boolean contains(String id) { + public boolean contains(@Nullable String id) { return mTotalSelection.contains(id); } @@ -804,11 +814,6 @@ public final class MultiSelectManager implements View.OnKeyListener { int getChildCount(); int getVisibleChildCount(); void focusItem(int position); - String getModelIdFromAdapterPosition(int position); - int getItemCount(); - List<String> getModelIds(); - void notifyItemChanged(String id); - void registerDataObserver(RecyclerView.AdapterDataObserver observer); } /** Recycler view facade implementation backed by good ol' RecyclerView. */ @@ -818,11 +823,9 @@ public final class MultiSelectManager implements View.OnKeyListener { private final Drawable mBand; private boolean mIsOverlayShown = false; - private DirectoryFragment.DocumentsAdapter mAdapter; - RuntimeSelectionEnvironment(RecyclerView rv) { - mView = rv; - mAdapter = (DirectoryFragment.DocumentsAdapter) rv.getAdapter(); + RuntimeSelectionEnvironment(RecyclerView view) { + mView = view; mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay); } @@ -841,11 +844,6 @@ public final class MultiSelectManager implements View.OnKeyListener { } @Override - public String getModelIdFromAdapterPosition(int position) { - return mAdapter.getModelId(position); - } - - @Override public void addOnScrollListener(RecyclerView.OnScrollListener listener) { mView.addOnScrollListener(listener); } @@ -961,26 +959,6 @@ public final class MultiSelectManager implements View.OnKeyListener { }); } } - - @Override - public void notifyItemChanged(String id) { - mAdapter.notifyItemChanged(id, SELECTION_CHANGED_MARKER); - } - - @Override - public int getItemCount() { - return mAdapter.getItemCount(); - } - - @Override - public void registerDataObserver(RecyclerView.AdapterDataObserver observer) { - mAdapter.registerAdapterDataObserver(observer); - } - - @Override - public List<String> getModelIds() { - return mAdapter.getModelIds(); - } } public interface Callback { @@ -1037,7 +1015,7 @@ public final class MultiSelectManager implements View.OnKeyListener { mModelBuilder = new Runnable() { @Override public void run() { - mModel = new GridModel(mEnvironment); + mModel = new GridModel(mEnvironment, mAdapter); mModel.addOnSelectionChangedListener(BandController.this); } }; @@ -1088,7 +1066,7 @@ public final class MultiSelectManager implements View.OnKeyListener { return !isActive() && e.isMouseEvent() // a mouse && e.isActionDown() // the initial button press - && mEnvironment.getItemCount() > 0 + && mAdapter.getItemCount() > 0 && e.getItemPosition() == RecyclerView.NO_ID; // in empty space } @@ -1165,7 +1143,7 @@ public final class MultiSelectManager implements View.OnKeyListener { mModel.endSelection(); int firstSelected = mModel.getPositionNearestOrigin(); if (firstSelected != NOT_SET) { - if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(firstSelected))) { + if (mSelection.contains(mAdapter.getModelId(firstSelected))) { // TODO: firstSelected should really be lastSelected, we want to anchor the item // where the mouse-up occurred. setSelectionRangeBegin(firstSelected); @@ -1327,6 +1305,8 @@ public final class MultiSelectManager implements View.OnKeyListener { private static final int LOWER_RIGHT = LOWER | RIGHT; private final SelectionEnvironment mHelper; + private final DocumentsAdapter mAdapter; + private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>(); @@ -1364,8 +1344,9 @@ public final class MultiSelectManager implements View.OnKeyListener { // should expand from when Shift+click is used. private int mPositionNearestOrigin = NOT_SET; - GridModel(SelectionEnvironment helper) { + GridModel(SelectionEnvironment helper, DocumentsAdapter adapter) { mHelper = helper; + mAdapter = adapter; mHelper.addOnScrollListener(this); } @@ -1568,7 +1549,7 @@ public final class MultiSelectManager implements View.OnKeyListener { // position. Use a sentry value to prevent erroneously selecting item 0. int position = items.get(items.keyAt(row), NOT_SET); if (position != NOT_SET) { - String id = mHelper.getModelIdFromAdapterPosition(position); + String id = mAdapter.getModelId(position); if (id != null) { // The adapter inserts items for UI layout purposes that aren't associated // with files. Those will have a null model ID. Don't select them. @@ -1977,7 +1958,7 @@ public final class MultiSelectManager implements View.OnKeyListener { if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) { position = 0; } else if (keyCode == KeyEvent.KEYCODE_MOVE_END) { - position = mEnvironment.getItemCount() - 1; + position = mAdapter.getItemCount() - 1; } else { // Find a navigation target based on the arrow key that the user pressed. Ignore // navigation targets that aren't items in the recycler view. diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java new file mode 100644 index 000000000000..b4782f035e11 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import static com.android.internal.util.Preconditions.checkArgument; + +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView.AdapterDataObserver; +import android.util.SparseArray; +import android.view.ViewGroup; + +import java.util.List; + +/** + * Adapter wrapper that inserts a sort of line break item between directories and regular files. + * Only needs to be used in GRID mode...at this time. + */ +final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter { + + private static final String TAG = "SectionBreakDocumentsAdapterWrapper"; + private static final int ITEM_TYPE_SECTION_BREAK = Integer.MAX_VALUE; + + private final Environment mEnv; + private final DocumentsAdapter mDelegate; + + private int mBreakPosition = -1; + + SectionBreakDocumentsAdapterWrapper(Environment environment, DocumentsAdapter delegate) { + mEnv = environment; + mDelegate = delegate; + + // Relay events published by our delegate to our listeners (presumably RecyclerView) + // with adjusted positions. + mDelegate.registerAdapterDataObserver(new EventRelay()); + } + + public GridLayoutManager.SpanSizeLookup createSpanSizeLookup() { + return new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + // Make layout whitespace span the grid. This has the effect of breaking + // grid rows whenever layout whitespace is encountered. + if (getItemViewType(position) == ITEM_TYPE_SECTION_BREAK) { + return mEnv.getColumnCount(); + } else { + return 1; + } + } + }; + } + + @Override + public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == ITEM_TYPE_SECTION_BREAK) { + return new EmptyDocumentHolder(mEnv.getContext()); + } else { + return mDelegate.createViewHolder(parent, viewType); + } + } + + @Override + public void onBindViewHolder(DocumentHolder holder, int p, List<Object> payload) { + if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) { + mDelegate.onBindViewHolder(holder, toDelegatePosition(p), payload); + } + } + + @Override + public void onBindViewHolder(DocumentHolder holder, int p) { + if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) { + mDelegate.onBindViewHolder(holder, toDelegatePosition(p)); + } + } + + @Override + public int getItemCount() { + return mBreakPosition == -1 + ? mDelegate.getItemCount() + : mDelegate.getItemCount() + 1; + } + + @Override + public void onModelUpdate(Model model) { + mDelegate.onModelUpdate(model); + mBreakPosition = -1; + + // Walk down the list of IDs till we encounter something that's not a directory, and + // insert a whitespace element - this introduces a visual break in the grid between + // folders and documents. + // TODO: This code makes assumptions about the model, namely, that it performs a + // bucketed sort where directories will always be ordered before other files. CBB. + List<String> modelIds = mDelegate.getModelIds(); + for (int i = 0; i < modelIds.size(); i++) { + if (!isDirectory(model, i)) { + mBreakPosition = i; + break; + } + } + } + + @Override + public void onModelUpdateFailed(Exception e) { + mDelegate.onModelUpdateFailed(e); + } + + @Override + public int getItemViewType(int p) { + if (p == mBreakPosition) { + return ITEM_TYPE_SECTION_BREAK; + } else { + return mDelegate.getItemViewType(toDelegatePosition(p)); + } + } + + /** + * Returns the position of an item in the delegate, adjusting + * values that are greater than the break position. + * + * @param p Position within the view + * @return Position within the delegate + */ + private int toDelegatePosition(int p) { + return (mBreakPosition != -1 && p > mBreakPosition) ? p - 1 : p; + } + + /** + * Returns the position of an item in the view, adjusting + * values that are greater than the break position. + * + * @param p Position within the delegate + * @return Position within the view + */ + private int toViewPosition(int p) { + // If position is greater than or equal to the break, increase by one. + return (mBreakPosition != -1 && p >= mBreakPosition) ? p + 1 : p; + } + + @Override + public SparseArray<String> hide(String... ids) { + // NOTE: We hear about these changes and adjust break position + // in our AdapterDataObserver. + return mDelegate.hide(ids); + } + + @Override + void unhide(SparseArray<String> ids) { + // NOTE: We hear about these changes and adjust break position + // in our AdapterDataObserver. + mDelegate.unhide(ids); + } + + @Override + List<String> getModelIds() { + return mDelegate.getModelIds(); + } + + @Override + String getModelId(int p) { + return (p == mBreakPosition) ? null : mDelegate.getModelId(toDelegatePosition(p)); + } + + @Override + public void onItemSelectionChanged(String id) { + mDelegate.onItemSelectionChanged(id); + } + + // Listener we add to our delegate. This allows us to relay events published + // by the delegate to our listeners (presumably RecyclerView) with adjusted positions. + private final class EventRelay extends AdapterDataObserver { + public void onChanged() { + throw new UnsupportedOperationException(); + } + + public void onItemRangeChanged(int positionStart, int itemCount) { + throw new UnsupportedOperationException(); + } + + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + checkArgument(itemCount == 1); + notifyItemRangeChanged(toViewPosition(positionStart), itemCount, payload); + } + + public void onItemRangeInserted(int positionStart, int itemCount) { + checkArgument(itemCount == 1); + if (positionStart < mBreakPosition) { + mBreakPosition++; + } + notifyItemRangeInserted(toViewPosition(positionStart), itemCount); + } + + public void onItemRangeRemoved(int positionStart, int itemCount) { + checkArgument(itemCount == 1); + if (positionStart < mBreakPosition) { + mBreakPosition--; + } + notifyItemRangeRemoved(toViewPosition(positionStart), itemCount); + } + + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java index 4caa891fa24e..12c0b8fcd228 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java @@ -174,7 +174,7 @@ public class RootInfo implements Durable, Parcelable { derivedIcon = R.drawable.ic_root_home; derivedType = TYPE_LOCAL; } else if (isExternalStorage()) { - derivedIcon = R.drawable.ic_root_sdcard; + derivedIcon = R.drawable.ic_root_smartphone; derivedType = TYPE_LOCAL; } else if (isDownloads()) { derivedIcon = R.drawable.ic_root_download; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java new file mode 100644 index 000000000000..5ce1823d6a21 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import android.content.Context; +import android.database.Cursor; +import android.support.v7.widget.RecyclerView; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.SparseArray; +import android.view.ViewGroup; + +import com.android.documentsui.State; + +import java.util.List; + +@SmallTest +public class ModelBackedDocumentsAdapterTest extends AndroidTestCase { + + private static final String AUTHORITY = "test_authority"; + private static final String[] NAMES = new String[] { + "4", + "foo", + "1", + "bar", + "*(Ljifl;a", + "0", + "baz", + "2", + "3", + "%$%VD" + }; + + private TestModel mModel; + private ModelBackedDocumentsAdapter mAdapter; + + public void setUp() { + + final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY); + mModel = new TestModel(testContext, AUTHORITY); + mModel.update(NAMES); + + DocumentsAdapter.Environment env = new TestEnvironment(testContext); + + mAdapter = new ModelBackedDocumentsAdapter( + env, new IconHelper(testContext, State.MODE_GRID)); + mAdapter.onModelUpdate(mModel); + } + + // Tests that the item count is correct. + public void testItemCount() { + assertEquals(mModel.getItemCount(), mAdapter.getItemCount()); + } + + // Tests that the item count is correct. + public void testHide_ItemCount() { + List<String> ids = mModel.getModelIds(); + mAdapter.hide(ids.get(0), ids.get(1)); + assertEquals(mModel.getItemCount() - 2, mAdapter.getItemCount()); + } + + // Tests that the items can be hidden and unhidden. + public void testUnhide_ItemCount() { + List<String> ids = mModel.getModelIds(); + SparseArray<String> hidden = mAdapter.hide(ids.toArray(new String[ids.size()])); + mAdapter.unhide(hidden); + assertEquals(mModel.getItemCount(), mAdapter.getItemCount()); + } + + // Tests that the items can be hidden and unhidden. + public void testUnhide_PreservesOrder() { + List<String> ids = mModel.getModelIds(); + SparseArray<String> hidden = mAdapter.hide( + ids.get(0), ids.get(1), ids.get(5), ids.get(9)); + mAdapter.unhide(hidden); + + // Finally ensure the restored items are in the original order + // by checking them against the model. + for (int i = 0; i < mAdapter.getItemCount(); i++) { + assertEquals(mModel.idForPosition(i), mAdapter.getModelId(i)); + } + } + + private final class TestEnvironment implements DocumentsAdapter.Environment { + private final Context testContext; + + private TestEnvironment(Context testContext) { + this.testContext = testContext; + } + + @Override + public boolean isSelected(String id) { + return false; + } + + @Override + public boolean isDocumentEnabled(String mimeType, int flags) { + return true; + } + + @Override + public void initDocumentHolder(DocumentHolder holder) {} + + @Override + public Model getModel() { + return mModel; + } + + @Override + public State getDisplayState() { + return null; + } + + @Override + public Context getContext() { + return testContext; + } + + @Override + public int getColumnCount() { + return 4; + } + + @Override + public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) {} + } + + private static class DummyListener implements Model.UpdateListener { + public void onModelUpdate(Model model) {} + public void onModelUpdateFailed(Exception e) {} + } + + private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + public int getItemCount() { return 0; } + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {} + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return null; + } + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java index 121eb41c4c2a..bed7c9c9a253 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java @@ -21,16 +21,10 @@ import android.content.Context; import android.content.ContextWrapper; import android.database.Cursor; import android.database.MatrixCursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; -import android.support.v7.widget.RecyclerView; import android.test.AndroidTestCase; -import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.SmallTest; -import android.view.ViewGroup; import com.android.documentsui.DirectoryResult; import com.android.documentsui.RootCursorWrapper; @@ -49,6 +43,7 @@ public class ModelTest extends AndroidTestCase { private static final int ITEM_COUNT = 10; private static final String AUTHORITY = "test_authority"; + private static final String[] COLUMNS = new String[]{ RootCursorWrapper.COLUMN_AUTHORITY, Document.COLUMN_DOCUMENT_ID, @@ -57,23 +52,24 @@ public class ModelTest extends AndroidTestCase { Document.COLUMN_SIZE, Document.COLUMN_MIME_TYPE }; - private static Cursor cursor; + private static final String[] NAMES = new String[] { + "4", + "foo", + "1", + "bar", + "*(Ljifl;a", + "0", + "baz", + "2", + "3", + "%$%VD" + }; + + private Cursor cursor; private Context context; private Model model; private TestContentProvider provider; - private static final String[] NAMES = new String[] { - "4", - "foo", - "1", - "bar", - "*(Ljifl;a", - "0", - "baz", - "2", - "3", - "%$%VD" - }; public void setUp() { setupTestContext(); @@ -97,7 +93,7 @@ public class ModelTest extends AndroidTestCase { r.cursor = cursor; // Instantiate the model with a dummy view adapter and listener that (for now) do nothing. - model = new Model(context, new DummyAdapter()); + model = new Model(context); model.addUpdateListener(new DummyListener()); model.update(r); } @@ -326,32 +322,4 @@ public class ModelTest extends AndroidTestCase { public void onModelUpdate(Model model) {} public void onModelUpdateFailed(Exception e) {} } - - private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { - public int getItemCount() { return 0; } - public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {} - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return null; - } - } - - private static class TestContentProvider extends MockContentProvider { - List<Uri> mDeleted = new ArrayList<>(); - - @Override - public Bundle call(String method, String arg, Bundle extras) { - // Intercept and log delete method calls. - if (DocumentsContract.METHOD_DELETE_DOCUMENT.equals(method)) { - final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI); - mDeleted.add(documentUri); - return new Bundle(); - } else { - return super.call(method, arg, extras); - } - } - - public void assertWasDeleted(DocumentInfo doc) { - assertTrue(mDeleted.contains(doc.derivedUri)); - } - } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java index 59891350838e..e06199efbc3b 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java @@ -23,6 +23,7 @@ import android.util.SparseBooleanArray; import com.android.documentsui.TestInputEvent; import com.android.documentsui.dirlist.MultiSelectManager.Selection; + import com.google.common.collect.Lists; import java.util.ArrayList; @@ -44,11 +45,13 @@ public class MultiSelectManagerTest extends AndroidTestCase { private MultiSelectManager mManager; private TestCallback mCallback; private TestSelectionEnvironment mEnv; + private TestDocumentsAdapter mAdapter; public void setUp() throws Exception { mCallback = new TestCallback(); mEnv = new TestSelectionEnvironment(items); - mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_MULTIPLE); + mAdapter = new TestDocumentsAdapter(items); + mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_MULTIPLE); mManager.addCallback(mCallback); } @@ -164,7 +167,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } public void testSingleSelectMode() { - mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE); + mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE); mManager.addCallback(mCallback); longPress(20); tap(13); @@ -172,7 +175,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } public void testSingleSelectMode_ShiftTap() { - mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE); + mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE); mManager.addCallback(mCallback); longPress(13); shiftTap(20); @@ -180,7 +183,7 @@ public class MultiSelectManagerTest extends AndroidTestCase { } public void testSingleSelectMode_ShiftDoesNotExtendSelection() { - mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE); + mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE); mManager.addCallback(mCallback); longPress(20); keyToPosition(22, true); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java index 3d6923fbc984..5c04db9d513e 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java @@ -20,7 +20,6 @@ import static com.android.documentsui.dirlist.MultiSelectManager.GridModel.NOT_S import android.graphics.Point; import android.graphics.Rect; -import android.support.v7.widget.RecyclerView.AdapterDataObserver; import android.support.v7.widget.RecyclerView.OnScrollListener; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -28,7 +27,7 @@ import android.view.View; import com.android.documentsui.dirlist.MultiSelectManager.GridModel; -import java.util.List; +import java.util.ArrayList; import java.util.Set; @SmallTest @@ -38,15 +37,28 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { private static final int CHILD_VIEW_EDGE_PX = 100; private static final int VIEWPORT_HEIGHT = 500; - private static GridModel model; - private static TestEnvironment env; - private static Set<String> lastSelection; - private static int viewWidth; + private GridModel model; + private TestEnvironment env; + private TestDocumentsAdapter adapter; + private Set<String> lastSelection; + private int viewWidth; - private static void setUp(int numChildren, int numColumns) { + private void initData(final int numChildren, int numColumns) { env = new TestEnvironment(numChildren, numColumns); + adapter = new TestDocumentsAdapter(new ArrayList<String>()) { + @Override + public String getModelId(int position) { + return Integer.toString(position); + } + + @Override + public int getItemCount() { + return numChildren; + } + }; + viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX); - model = new GridModel(env); + model = new GridModel(env, adapter); model.addOnSelectionChangedListener( new GridModel.OnSelectionChangedListener() { @Override @@ -64,7 +76,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionLeftOfItems() { - setUp(20, 5); + initData(20, 5); model.startSelection(new Point(0, 10)); model.resizeSelection(new Point(1, 11)); assertSelected(); @@ -72,7 +84,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionRightOfItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(viewWidth - 1, 10)); model.resizeSelection(new Point(viewWidth - 2, 11)); assertSelected(); @@ -80,7 +92,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionAboveItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(10, 0)); model.resizeSelection(new Point(11, 1)); assertSelected(); @@ -88,7 +100,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionBelowItems() { - setUp(5, 4); + initData(5, 4); model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1)); model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2)); assertSelected(); @@ -96,7 +108,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testVerticalSelectionBetweenItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(106, 0)); model.resizeSelection(new Point(107, 200)); assertSelected(); @@ -104,7 +116,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testHorizontalSelectionBetweenItems() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(0, 105)); model.resizeSelection(new Point(200, 106)); assertSelected(); @@ -112,7 +124,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testGrowingAndShrinkingSelection() { - setUp(20, 4); + initData(20, 4); model.startSelection(new Point(0, 0)); model.resizeSelection(new Point(5, 5)); assertSelected(0); @@ -142,7 +154,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testSelectionMovingAroundOrigin() { - setUp(16, 4); + initData(16, 4); model.startSelection(new Point(210, 210)); model.resizeSelection(new Point(viewWidth - 1, 0)); assertSelected(2, 3, 6, 7); @@ -156,7 +168,7 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { } public void testScrollingBandSelect() { - setUp(40, 4); + initData(40, 4); model.startSelection(new Point(0, 0)); model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); assertSelected(0, 4, 8, 12, 16); @@ -173,14 +185,14 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { assertEquals(0, model.getPositionNearestOrigin()); } - private static void assertSelected(int... selectedPositions) { + private void assertSelected(int... selectedPositions) { assertEquals(selectedPositions.length, lastSelection.size()); for (int position : selectedPositions) { assertTrue(lastSelection.contains(Integer.toString(position))); } } - private static void scroll(int dy) { + private void scroll(int dy) { assertTrue(env.verticalOffset + VIEWPORT_HEIGHT + dy <= env.getTotalHeight()); env.verticalOffset += dy; model.onScrolled(null, 0, dy); @@ -322,30 +334,5 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { public void focusItem(int i) { throw new UnsupportedOperationException(); } - - @Override - public String getModelIdFromAdapterPosition(int position) { - return Integer.toString(position); - } - - @Override - public int getItemCount() { - return mNumChildren; - } - - @Override - public List<String> getModelIds() { - return null; - } - - @Override - public void notifyItemChanged(String id) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerDataObserver(AdapterDataObserver observer) { - throw new UnsupportedOperationException(); - } } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java new file mode 100644 index 000000000000..c8d424f10eae --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import android.net.Uri; +import android.os.Bundle; +import android.provider.DocumentsContract; +import android.test.mock.MockContentProvider; + +import com.android.documentsui.model.DocumentInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * A very simple test double for ContentProvider. Useful in this package only. + */ +class TestContentProvider extends MockContentProvider { + List<Uri> mDeleted = new ArrayList<>(); + + @Override + public Bundle call(String method, String arg, Bundle extras) { + // Intercept and log delete method calls. + if (DocumentsContract.METHOD_DELETE_DOCUMENT.equals(method)) { + final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI); + mDeleted.add(documentUri); + return new Bundle(); + } else { + return super.call(method, arg, extras); + } + } + + public void assertWasDeleted(DocumentInfo doc) { + ModelTest.assertTrue(mDeleted.contains(doc.derivedUri)); + } +}
\ No newline at end of file diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java new file mode 100644 index 000000000000..714062d5007c --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; +import android.test.mock.MockContentResolver; + +public final class TestContext { + + /** + * Returns a Context configured with test provider for authority. + */ + static Context createStorageTestContext(Context context, String authority) { + final MockContentResolver testResolver = new MockContentResolver(); + TestContentProvider provider = new TestContentProvider(); + testResolver.addProvider(authority, provider); + + return new ContextWrapper(context) { + @Override + public ContentResolver getContentResolver() { + return testResolver; + } + }; + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java new file mode 100644 index 000000000000..267f47d80d3f --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import android.util.SparseArray; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +/** + * A skeletal {@link DocumentsAdapter} test double. + */ +public class TestDocumentsAdapter extends DocumentsAdapter { + + List<String> mModelIds = new ArrayList<>(); + + public TestDocumentsAdapter(List<String> modelIds) { + mModelIds = modelIds; + } + + @Override + public void onModelUpdate(Model model) { + } + + @Override + public void onModelUpdateFailed(Exception e) { + } + + @Override + List<String> getModelIds() { + return mModelIds; + } + + @Override + void onItemSelectionChanged(String id) { + } + + @Override + String getModelId(int position) { + return mModelIds.get(position); + } + + @Override + public SparseArray<String> hide(String... ids) { + throw new UnsupportedOperationException(); + } + + @Override + void unhide(SparseArray<String> ids) { + throw new UnsupportedOperationException(); + } + + @Override + public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) { + throw new UnsupportedOperationException(); + } + + @Override + public void onBindViewHolder(DocumentHolder holder, int position) { + throw new UnsupportedOperationException(); + } + + @Override + public int getItemCount() { + return mModelIds.size(); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java new file mode 100644 index 000000000000..3a537a6f5ea3 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.dirlist; + +import android.content.Context; +import android.database.MatrixCursor; +import android.provider.DocumentsContract.Document; + +import com.android.documentsui.DirectoryResult; +import com.android.documentsui.RootCursorWrapper; +import com.android.documentsui.dirlist.MultiSelectManager.Selection; + +import java.util.Random; +import java.util.Set; + +public class TestModel extends Model { + + private static final String[] COLUMNS = new String[]{ + RootCursorWrapper.COLUMN_AUTHORITY, + Document.COLUMN_DOCUMENT_ID, + Document.COLUMN_FLAGS, + Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_SIZE, + Document.COLUMN_MIME_TYPE + }; + + private final String mAuthority; + private Set<String> mDeleted; + + /** + * Creates a new context. context must be configured with provider for authority. + * @see TestContext#createStorageTestContext(Context, String). + */ + public TestModel(Context context, String authority) { + super(context); + mAuthority = authority; + } + + void update(String... names) { + Random rand = new Random(); + + MatrixCursor c = new MatrixCursor(COLUMNS); + for (int i = 0; i < names.length; i++) { + MatrixCursor.RowBuilder row = c.newRow(); + row.add(RootCursorWrapper.COLUMN_AUTHORITY, mAuthority); + row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i)); + row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_DELETE); + // Generate random document names and sizes. This forces the model's internal sort code + // to actually do something. + row.add(Document.COLUMN_DISPLAY_NAME, names[i]); + row.add(Document.COLUMN_SIZE, rand.nextInt()); + } + + DirectoryResult r = new DirectoryResult(); + r.cursor = c; + update(r); + } + + // Note that model id includes authority qualifier and is distinct + // WRT documentId because of this. + String idForPosition(int p) { + return createModelId(mAuthority, Integer.toString(p)); + } + + @Override + public void delete(Selection selected, DeletionListener listener) { + for (String id : selected.getAll()) { + mDeleted.add(id); + } + listener.onCompletion(); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java index fc85f2b2962e..c4cfd3a06104 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java @@ -18,7 +18,6 @@ package com.android.documentsui.dirlist; import android.graphics.Point; import android.graphics.Rect; -import android.support.v7.widget.RecyclerView.AdapterDataObserver; import android.support.v7.widget.RecyclerView.OnScrollListener; import android.view.View; @@ -28,10 +27,7 @@ import java.util.List; public class TestSelectionEnvironment implements SelectionEnvironment { - private List<String> mItems; - public TestSelectionEnvironment(List<String> items) { - mItems = items; } @Override @@ -114,27 +110,4 @@ public class TestSelectionEnvironment implements SelectionEnvironment { @Override public void focusItem(int position) { } - - @Override - public String getModelIdFromAdapterPosition(int position) { - return mItems.get(position); - } - - @Override - public int getItemCount() { - return mItems.size(); - } - - @Override - public List<String> getModelIds() { - return null; - } - - @Override - public void notifyItemChanged(String id) { - } - - @Override - public void registerDataObserver(AdapterDataObserver observer) { - } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java index 82fd51233ef2..3dc5d7eccdf6 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java @@ -158,11 +158,14 @@ final class NotificationController { builder.addAction(secondAction); } - if (printJob.getState() == PrintJobInfo.STATE_STARTED) { + if (printJob.getState() == PrintJobInfo.STATE_STARTED + || printJob.getState() == PrintJobInfo.STATE_QUEUED) { float progress = printJob.getProgress(); if (progress >= 0) { - builder.setProgress(Integer.MAX_VALUE, (int)(Integer.MAX_VALUE * progress), + builder.setProgress(Integer.MAX_VALUE, (int) (Integer.MAX_VALUE * progress), false); + } else { + builder.setProgress(Integer.MAX_VALUE, 0, true); } } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 0d9d79ce19f0..ebe8752777f6 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Laat enige program na ekstern geskryf word, ongeag manifeswaardes"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Verplig verstelbare groottes vir aktiwiteite"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Maak grootte van alle aktiwiteite verstelbaar vir veelvuldige vensters, ongeag manifeswaardes."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Aktiveer vormvrye vensters"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiveer steun vir eksperimentele vormvrye vensters."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Werkskerm-rugsteunwagwoord"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Volle rekenaarrugsteune word nie tans beskerm nie"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Raak om die wagwoord vir volledige rekenaarrugsteune te verander of te verwyder"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 1c9b0a9d6f54..e33d0fa9dace 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"አንጸባራቂ እሴቶች ግምት ውስጥ ሳይገቡ ማንኛውም መተግበሪያ ወደ ውጫዊ ማከማቻ ለመጻፍ ብቁ ያደርጋል።"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"እንቅስቃሴዎች ዳግመኛ እንዲመጣጠኑ አስገድድ"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"የዝርዝር ሰነድ እሴቶች ምንም ይሁኑ ምን ለበርካታ መስኮቶች ሁሉንም እንቅስቃሴዎች ዳግም የሚመጣጠኑ እንዲሆኑ ያደርጋቸዋል።"</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"የነጻ ቅርጽ መስኮቶችን ያንቁ"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"የሙከራ ነጻ ቅርጽ መስኮቶች ድጋፍን ያነቃል።"</string> <string name="local_backup_password_title" msgid="3860471654439418822">"የዴስክቶፕ መጠባበቂያ ይለፍ ቃል"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ዴስክቶፕ ሙሉ ምትኬዎች በአሁኑ ሰዓት አልተጠበቁም"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ለዴስክቶፕ ሙሉ ምትኬዎች የይለፍ ቃል ለመለወጥ ወይም ለማስወገድ ንካ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index fc11a6b6fab3..71761b99a235 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"تأهيل أي تطبيق بحيث تتم كتابته على سعة تخزين خارجية، بغض النظر عن قيم البيان"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"فرض إمكانية تغيير على الأنشطة"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"لتمكين تغيير حجم جميع الأنشطة لتناسب تعدد النوافذ، بغض النظر عن قيم البيان."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"تمكين النوافذ الحرة"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"لتمكين إتاحة استخدام النوافذ الحرة التجريبية."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"كلمة مرور احتياطية للكمبيوتر"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"النسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"المس لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها"</string> diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml index 4a5ea375b056..b5a718116e30 100644 --- a/packages/SettingsLib/res/values-az-rAZ/strings.xml +++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Seçilmiş hər hansı tətbiqi bəyannamə dəyərlərindən aslı olmayaraq xarici yaddaşa yazılabilən edir."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Ölçü dəyişdirmək üçün məcburi fəaliyyətlər"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Bəyannamə dəyərlərindən aslı olmayaraq bütün fəaliyyətləri çoxsaylı pəncərə üçün dəyişkən ölçülü edir."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Freeform windows aktiv edin"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Sınaq üçün freeform windows aktiv edir"</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Masaüstü rezerv parolu"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Masaüstü tam rezervlər hazırda qorunmayıblar."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Masaüstünün tam rezevr kopyalanması üçün parolu dəyişmək və ya silmək üçün toxunun"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 676c618389fe..54d2dfa3c8ef 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Omogućava upisivanje svih aplikacija u spoljnu memoriju, bez obzira na vrednosti manifesta"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Prinudno omogući promenu veličine aktivnosti"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Omogućava promenu veličine svih aktivnosti za režim sa više prozora, bez obzira na vrednosti manifesta."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka rezervne kopije za računar"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervne kopije čitavog sistema trenutno nisu zaštićene"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dodirnite da biste promenili ili uklonili lozinku za pravljenje rezervnih kopija čitavog sistema na računaru"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 8180261bb619..95badc57f108 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Позволява прилож. да се записват във външ. хранил. независимо от стойностите в манифеста"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Възможност за преоразмеряване на активностите"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Дава възможност за преоразмеряване на всички активности в режима за няколко прозореца независимо от стойностите в манифеста."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Наст. комп.: Парола"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Понастоящем пълните резервни копия за настолен компютър не са защитени"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Докоснете, за да промените или премахнете паролата за пълни резервни копия на настолния компютър"</string> diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml index a5787f5cc1da..e4d97d7e8516 100644 --- a/packages/SettingsLib/res/values-bn-rBD/strings.xml +++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ম্যানিফেস্ট মানগুলি নির্বিশেষে যেকোনো অ্যাপ্লিকেশানকে বাহ্যিক সঞ্চয়স্থানে লেখার উপযুক্ত বানায়"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"আকার পরিবর্তনযোগ্য করার জন্য ক্রিয়াকলাপগুলিকে জোর করুন"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ম্যানিফেস্ট মানগুলির নির্বিশেষে মাল্টি-উইন্ডোর জন্য সমস্ত ক্রিয়াকলাপগুলিকে আকার পরিবর্তনযোগ্য করে তোলে৷"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ডেস্কটপ ব্যাকআপ পাসওয়ার্ড"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ডেস্কটপ পূর্ণ ব্যাকআপ বর্তমানে সুরক্ষিত নয়"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ডেস্কটপ পুরো ব্যাকআপের জন্য পাসওয়ার্ড পরিবর্তন বা মুছে ফেলার জন্য স্পর্শ করুন"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 8553ed9f5ad1..93b6438b37c0 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet que les aplicacions es puguin escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet ajustar la mida de totes les activitats per al mode multifinestra, independentment dels valors definits."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Contrasenya per a còpies d\'ordinador"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les còpies de seguretat d\'ordinador completes no estan protegides"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca per canviar o eliminar la contrasenya per a còpies de seguretat d\'ordinador completes"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 495f10fa3729..beb8f94abebc 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Každou aplikaci bude možné zapsat do externího úložiště, bez ohledu na hodnoty manifestu"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Vynutit možnost změny velikosti aktivit"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Velikost všech aktivit bude možné změnit na několik oken (bez ohledu na hodnoty manifestu)."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivovat okna s volným tvarem"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktivuje podporu experimentálních oken s volným tvarem."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Heslo pro zálohy v počítači"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Úplné zálohy v počítači nejsou v současné době chráněny"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tuto možnost vyberte, chcete-li změnit nebo odebrat heslo pro úplné zálohy v počítači"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 82654bcc0a66..3923dbd7326b 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gør det muligt at overføre enhver app til et eksternt lager uafhængigt af manifestværdier"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til at kunne tilpasses"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Sørger for, at alle aktiviteter kan tilpasses flere vinduer uafhængigt af manifestværdier."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivér vinduer i frit format"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiverer understøttelse af eksperimentelle vinduer i frit format."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Kode til lokal sikkerhedskopi"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Lokale fuldstændige sikkerhedskopieringer er i øjeblikket ikke beskyttet"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tryk for at skifte eller fjerne adgangskoden til fuld lokal sikkerhedskopiering"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index a2f750b06708..0b1238b84ffb 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ermöglicht es jeder qualifizierten App, Daten auf externen Speicher zu schreiben, unabhängig von den Manifestwerten"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Anpassen der Größe von Aktivitäten erzwingen"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Ermöglicht es, die Größe aller Aktivitäten an den Mehrfenstermodus anzupassen, unabhängig von den Manifestwerten."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop-Sicherungspasswort"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Vollständige Desktop-Sicherungen sind momentan nicht passwortgeschützt."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Zum Ändern oder Entfernen des Passworts für vollständige Desktop-Sicherungen berühren"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index d42661638f78..c3acca719754 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Κάνει κάθε εφαρμογή κατάλληλη για εγγραφή σε εξωτερικό χώρο αποθήκευσης, ανεξάρτητα από τις τιμές του μανιφέστου"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Αναγκαστική δυνατότητα αλλαγής μεγέθους δραστηριοτήτων"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Να έχουν όλες οι δραστηριότητες δυνατότητα αλλαγής μεγέθους για την προβολή πολλαπλών παραθύρων, ανεξάρτητα από τις τιμές του μανιφέστου."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Ενεργοποίηση παραθύρων ελεύθερης μορφής"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ενεργοποιεί την υποστήριξη για πειραματικά παράθυρα ελεύθερης μορφής."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Εφ/κός κωδικός desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας δεν προστατεύονται αυτήν τη στιγμή"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Αγγίξτε για αλλαγή ή κατάργηση του κωδικού πρόσβασης για τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index b8c852135549..dae40d91882c 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index b8c852135549..dae40d91882c 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index b8c852135549..dae40d91882c 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 34ebef451b62..855b72ca97b7 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Cualquier aplicación puede escribirse en una memoria externa, independientemente de los valores del manifiesto."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar actividades para que cambien de tamaño"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite que todas las actividades puedan cambiar de tamaño para el modo multiventana, sin importar los valores del manifiesto."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Contraseñas"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tus copias de seguridad de escritorio no están protegidas por contraseña."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca para cambiar o eliminar la contraseña de las copias de seguridad completas de tu escritorio."</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index cedd5825b6e2..858c1b6d31b1 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Hace que cualquier aplicación se pueda escribir en un dispositivo de almacenamiento externo, independientemente de los valores definidos"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar el ajuste de tamaño de las actividades"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite que se pueda ajustar el tamaño de todas las actividades para el modo multiventana, independientemente de los valores establecidos."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Habilitar ventanas de forma libre"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Permite utilizar ventanas de forma libre experimentales."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Contraseña para copias de ordenador"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Las copias de seguridad completas de ordenador no están protegidas"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tocar para cambiar o quitar la contraseña para las copias de seguridad completas de ordenador"</string> diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml index 714004e1c409..7f1056ca077d 100644 --- a/packages/SettingsLib/res/values-et-rEE/strings.xml +++ b/packages/SettingsLib/res/values-et-rEE/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Lubab rakendusi kirjutada välisesse salvestusruumi olenemata manifesti väärtustest"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Muuda tegevuste suurused muudetavaks"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Muudab kõigi tegevuste suurused mitme aknaga vaates olenemata manifesti väärtustest muudetavaks."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Luba vabas vormis aknad"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Lubatakse katseliste vabas vormis akende tugi."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Arvutivarunduse parool"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Täielikud arvutivarundused pole praegu kaitstud"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Puudutage, et muuta või eemaldada täielike arvutivarunduste parool"</string> diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml index 7f47bf3fbe7f..fe5fc2945e99 100644 --- a/packages/SettingsLib/res/values-eu-rES/strings.xml +++ b/packages/SettingsLib/res/values-eu-rES/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikazioek kanpoko memorian idatz dezakete, manifestuaren balioak kontuan izan gabe"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Behartu jardueren tamaina doitu ahal izatea"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifestuan jartzen duena jartzen duela ere, jarduera guztien tamaina doitzeko aukera ematen du, hainbat leihotan erabili ahal izan daitezen."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Tokiko babeskop. pasahitza"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Une honetan, ordenagailuko babeskopia osoak ez daude babestuta."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ukitu ordenagailuko babeskopia osoak egiteko pasahitza aldatzeko edo kentzeko"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1f8b575cf667..e1a35f7ba7a3 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"بدون توجه به مقادیر مانیفست، هر برنامهای را برای نوشتن در حافظه خارجی واجد شرایط میکند"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"اجبار فعالیتها به قابل تغییر اندازه بودن"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"بدون درنظر گرفتن مقادیر مانیفست، همه فعالیتها را برای چندپنجره قابل تغییر اندازه میکند."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"گذرواژه پشتیبانگیری محلی"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"پشتیبانگیری کامل رایانه درحال حاضر محافظت نمیشود"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"برای تغییر یا حذف گذرواژه برای نسخههای پشتیبان کامل دسکتاپ لمس کنید"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index e4b4b93fd8ff..498f2045c0c8 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mahdollistaa sovellusten tallentamisen ulkoiseen tall.tilaan luettelosta riippumatta"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Pakota kaikki toiminnot hyväksymään koon muutos"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pakottaa kaikki toiminnot hyväksymään koon muuttamisen rinnakkaisnäkymään luettelon arvoista riippumatta."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Varmuuskop. salasana"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tietokoneen kaikkien tietojen varmuuskopiointia ei ole tällä hetkellä suojattu"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Muuta tai vaihda tietokoneen kaikkien tietojen varmuuskopioinnin salasana koskettamalla"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 66c54a196ddc..b32bfb7eb35d 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet enreg. d\'applis sur espace stockage externe"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer les activités à être redimensionnables"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index e56940a43312..0190454e52be 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Rend possible enregistrement de toute appli sur espace stockage externe, indépendamment valeurs fichier manifeste."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer possibilité de redimensionner les activités"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string> diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml index 9bcf77037279..0efe805be218 100644 --- a/packages/SettingsLib/res/values-gl-rES/strings.xml +++ b/packages/SettingsLib/res/values-gl-rES/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Fai que calquera aplicación se poida escribir nun almacenamento externo, independentemente dos valores expresados"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar o axuste do tamaño das actividades"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite axustar o tamaño de todas as actividades para o modo de varias ventás, independentemente dos valores definidos."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Activar ventás de forma libre"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activa a compatibilidade con ventás de forma libre experimentais."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Contrasinal para copias"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"As copias de seguridade de ordenador completas non están protexidas"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca para cambiar ou eliminar o contrasinal para as copias de seguranza completas do escritorio"</string> diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml index 6dff8faddc6c..4b72648244bb 100644 --- a/packages/SettingsLib/res/values-gu-rIN/strings.xml +++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"મેનિફેસ્ટ મૂલ્યોને ધ્યાનમાં લીધા સિવાય, કોઈપણ એપ્લિકેશનને બાહ્ય સ્ટોરેજ પર લખાવા માટે લાયક બનાવે છે"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"પ્રવૃત્તિઓને ફરીથી કદ યોગ્ય થવા માટે ફરજ પાડો"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"તમામ પ્રવૃત્તિઓને મલ્ટી-વિંડો માટે ફરીથી કદ બદલી શકે તેવી બનાવે છે, મેનીફેસ્ટ મુલ્યોને ધ્યાનમાં લીધા સિવાય."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"મુક્તાકાર વિંડોઝ સક્ષમ કરો"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"પ્રાયોગિક મુક્તાકાર વિંડોઝ માટે સમર્થનને સક્ષમ કરે છે."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"ડેસ્કટૉપ બેકઅપ પાસવર્ડ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ હાલમાં સુરક્ષિત નથી"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ્સ માટેનો પાસવર્ડ બદલવા અથવા દૂર કરવા માટે ટચ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9ce83fa48247..5224c380e125 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"इससे कोई भी ऐप मेनिफेस्ट मान अनदेखा करके, बाहरी मेमोरी पर लिखने योग्य बन जाता है"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"आकार बदले जाने के लिए गतिविधियों को बाध्य करें"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"एकाधिक-विंडो के लिए सभी गतिविधियों के आकार को बदले जाने योग्य बनाता है, चाहे मेनिफेस्ट मान कुछ भी हों."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"फ़्रीफ़ॉर्म विंडो सक्षम करें"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रयोगात्मक फ़्रीफ़ॉर्म विंडो का समर्थन सक्षम करती है."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटॉप बैकअप पासवर्ड"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप पूर्ण बैकअप वर्तमान में सुरक्षित नहीं हैं"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटॉप के पूर्ण बैकअप के पासवर्ड को बदलने या निकालने के लिए स्पर्श करें."</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index dd0217151bf6..5cde2332d91f 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikacije se mogu zapisivati u vanjsku pohranu neovisno o manifestu"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Nametni mogućnost promjene veličine za aktivnosti"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veličina svih aktivnosti može se mijenjati za više prozora, neovisno o vrijednostima manifesta."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Zaporka sigurnosne kopije"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Potpune sigurnosne kopije na stolnom računalu trenutačno nisu zaštićene"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Odaberite za promjenu ili uklanjanje zaporke u potpunim sigurnosnim kopijama na stolnom računalu"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 9b3a386c5abb..3559f1e23646 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Lehetővé teszi, hogy külső tárhelyre lehessen írni"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Tevékenységek átméretezésének kényszerítése"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Lehetővé teszi, hogy az összes tevékenység átméretezhető legyen a többablakos megjelenítés érdekében a jegyzékértékektől függetlenül."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Szabad formájú ablakok engedélyezése"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Engedélyezi a kísérleti jellegű, szabad formájú ablakok támogatását."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Asztali mentés jelszava"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Az asztali teljes biztonsági mentések jelenleg nem védettek."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Érintse meg, ha módosítaná vagy eltávolítaná a jelszót az asztali teljes mentésekhez"</string> diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml index 27db9fedbb7a..42c9a05c0221 100644 --- a/packages/SettingsLib/res/values-hy-rAM/strings.xml +++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Թույլ է տալիս պահել հավելվածը արտաքին սարքում՝ մանիֆեստի արժեքներից անկախ"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Ստիպել, որ ակտիվությունների չափերը լինեն փոփոխելի"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Բոլոր ակտիվությունների չափերը բազմապատուհան ռեժիմի համար դարձնել փոփոխելի՝ մանիֆեստի արժեքներից անկախ:"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Աշխատասեղանի պահուստավորման գաղտնաբառ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Աշխատասեղանի ամբողջական պահուստավորումները այժմ պաշտպանված չեն"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Աշխատասեղանի ամբողջական պահուստավորման համար ընտրել փոխել կամ հեռացնել գաղտնաբառը"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 0cea20637741..6988a732eb26 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksterna"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktivitas agar ukurannya dapat diubah"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Sandi cadangan desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Saat ini cadangan desktop penuh tidak dilindungi"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string> diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml index b3509ca2f39f..8d912e947fc3 100644 --- a/packages/SettingsLib/res/values-is-rIS/strings.xml +++ b/packages/SettingsLib/res/values-is-rIS/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gerir hvaða forriti sem er kleift að skrifa í ytri geymslu, burtséð frá gildum í upplýsingaskrá"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Þvinga breytanlega stærð virkni"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gerir stærð allrar virkni breytanlega svo að hún henti fyrir marga glugga, óháð gildum í upplýsingaskrá."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Aðgangsorð tölvuafritunar"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Heildarafritun á tölvu er ekki varin sem stendur."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Snertu til að breyta eða fjarlægja aðgangsorðið fyrir heildarafritun á tölvu"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 9f897f7ab0d8..2d98a7403041 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Rende l\'app idonea all\'installaz. su mem. esterna, senza considerare i valori manifest"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Imponi formato modificabile alle attività"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Rende il formato di tutte le attività modificabile per la modalità multi-finestra, indipendentemente dai valori manifest."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Attiva finestre a forma libera"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Attiva il supporto per le finestre a forma libera sperimentali."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Password di backup desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"I backup desktop completi non sono attualmente protetti."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tocca per modificare o rimuovere la password per i backup desktop completi"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 2d446120514f..e175208293ce 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"אלץ יכולת קביעת גודל של הפעילויות"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא קשר לערך המניפסט."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי מקומי"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"גע כדי לשנות או להסיר את הסיסמה עבור גיבויים מלאים בשולחן העבודה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 81a5eaf5cd04..bf9bf8a44c2b 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"マニフェストの値に関係なく、すべてのアプリを外部ストレージに書き込めるようになります"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"アクティビティをサイズ変更可能にする"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"マニフェストの値に関係なく、マルチウィンドウですべてのアクティビティのサイズを変更できるようになります。"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"PCバックアップパスワード"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"デスクトップのフルバックアップは現在保護されていません"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"デスクトップのフルバックアップ用のパスワードを変更または削除する場合にタップします"</string> @@ -263,9 +267,9 @@ <item msgid="8280754435979370728">"目に自然な色"</item> <item msgid="5363960654009010371">"デジタルコンテンツに最適な色"</item> </string-array> - <string name="inactive_apps_title" msgid="1317817863508274533">"無効なアプリ"</string> - <string name="inactive_app_inactive_summary" msgid="6768756967594202411">"無効。タップすると切り替わります。"</string> - <string name="inactive_app_active_summary" msgid="4512911571954375968">"有効。タップすると切り替わります。"</string> + <string name="inactive_apps_title" msgid="1317817863508274533">"休止中のアプリ"</string> + <string name="inactive_app_inactive_summary" msgid="6768756967594202411">"休止中。タップすると切り替わります。"</string> + <string name="inactive_app_active_summary" msgid="4512911571954375968">"実行中。タップすると切り替わります。"</string> <string name="runningservices_settings_title" msgid="8097287939865165213">"実行中のサービス"</string> <string name="runningservices_settings_summary" msgid="854608995821032748">"現在実行中のサービスを表示して制御する"</string> <string name="night_mode_title" msgid="2594133148531256513">"夜間モード"</string> diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml index e2dfd7e744f5..da9d204a80f7 100644 --- a/packages/SettingsLib/res/values-ka-rGE/strings.xml +++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"აპები ჩაიწერ. გარე მეხს.-ზე აღწ. ფაილის მნიშვნ. მიუხედ."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"ზომაცვლადი აქტივობების იძულება"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"მანიფესტის მნიშვნელობების მიუხედავად, ყველა აქტივობას მრავალი ფანჯრის რეჟიმისთვის ზომაცვლადად აქცევს."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"დესკტოპის სარეზერვო ასლის პაროლი"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"დესკტოპის სრული სარეზერვო ასლები ამჟამად დაცული არ არის"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"შეეხეთ დესკტოპის სრული სარეზერვო ასლების პაროლის შესაცვლელად ან წასაშლელად"</string> diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml index 1b8e17689bd1..549711c8a283 100644 --- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml +++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест мәндеріне қарамастан кез келген қолданбаны сыртқы жадқа жазуға жарамды етеді"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Әрекеттерді өлшемін өзгертуге болатын етуге мәжбүрлеу"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест мәндеріне қарамастан барлық әрекеттерді бірнеше терезе үшін өлшемін өзгертуге болатын етеді."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Компьютер үстелінің сақтық көшірмесі"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Жұмыс үстелінің сақтық көшірмелері қазір қорғалмаған"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Жұмыс үстелінің толық сақтық көшірмесінің кілтсөзін өзгерту немесе жою үшін түртіңіз"</string> diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml index 0186a68131a5..aed43652ae74 100644 --- a/packages/SettingsLib/res/values-km-rKH/strings.xml +++ b/packages/SettingsLib/res/values-km-rKH/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ធ្វើឲ្យកម្មវិធីទាំងឡាយមានសិទ្ធិសរសេរទៅកាន់ឧបករណ៍ផ្ទុកខាងក្រៅ ដោយមិនគិតពីតម្លៃជាក់លាក់"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"បង្ខំឲ្យសកម្មភាពអាចប្តូរទំហំបាន"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"កំណត់ឲ្យសកម្មភាពទាំងអស់អាចប្តូរទំហំបានសម្រាប់ពហុផ្ទាំងវិនដូ ដោយមិនគិតពីតម្លៃមេនីហ្វេសឡើយ។"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ពាក្យសម្ងាត់បម្រុងទុកលើផ្ទៃតុ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ការបម្រុងទុកពេញលេញលើផ្ទៃតុបច្ចុប្បន្នមិនត្រូវបានការពារទេ។"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ប៉ះ ដើម្បីប្ដូរ ឬលុបពាក្យសម្ងាត់សម្រាប់ការបម្រុងទុកពេញលេញលើផ្ទៃតុ"</string> diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml index 56f400614523..50e5955f272d 100644 --- a/packages/SettingsLib/res/values-kn-rIN/strings.xml +++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳನ್ನು ಪರಿಗಣಿಸದೇ, ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆಗೆ ಬರೆಯಲು ಅರ್ಹಗೊಳಿಸುತ್ತದೆ"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಒತ್ತಾಯ ಮಾಡಿ"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳನ್ನು ಪರಿಗಣಿಸದೇ, ಬಹು-ವಿಂಡೊಗೆ ಎಲ್ಲಾ ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಮಾಡುತ್ತದೆ."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"ಮುಕ್ತಸ್ವರೂಪದ ವಿಂಡೊಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ಪ್ರಾಯೋಗಿಕ ಮುಕ್ತಸ್ವರೂಪದ ವಿಂಡೊಗಳಿಗೆ ಬೆಂಬಲವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"ಡೆಸ್ಕ್ಟಾಪ್ ಬ್ಯಾಕಪ್ ಪಾಸ್ವರ್ಡ್"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ಡೆಸ್ಕ್ಟಾಪ್ನ ಪೂರ್ಣ ಬ್ಯಾಕಪ್ಗಳನ್ನು ಪ್ರಸ್ತುತ ರಕ್ಷಿಸಲಾಗಿಲ್ಲ"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ಡೆಸ್ಕ್ಟಾಪ್ನ ಪೂರ್ಣ ಬ್ಯಾಕಪ್ಗಳಿಗೆ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಬದಲಾಯಿಸಲು ಅಥವಾ ತೆಗೆದುಹಾಕಲು ಸ್ಪರ್ಶಿಸಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 6ad3efd49511..ea5dc0062f0f 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"매니페스트 값에 관계없이 앱을 외부 저장소에 작성"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"활동의 크기가 조정 가능하도록 설정"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"모든 활동을 매니페스트 값에 관계없이 멀티 윈도우용으로 크기 조정 가능하도록 설정"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"데스크톱 백업 비밀번호"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"데스크톱 전체 백업에 비밀번호가 설정되어 있지 않음"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"데스크톱 전체 백업에 대한 비밀번호를 변경하거나 삭제하려면 터치하세요."</string> diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml index bf37ac45b2fa..6a474066a166 100644 --- a/packages/SettingsLib/res/values-ky-rKG/strings.xml +++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест маанилерине карабастан бардык колдонмолорду тышкы сактагычка сактоого уруксат берет"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Аракеттердин өлчөмүн өзгөртүүнү мажбурлоо"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест маанилерине карабастан бардык аракеттерди мульти-терезеге өлчөмү өзгөртүлгүдөй кылат."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Компүтердеги бэкаптын сырсөзү"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Компүтердеги толук бэкап учурда корголгон эмес"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Тийип, компүтердеги толук бэкаптын сырсөзүн өзгөртүңүз же жок кылыңыз"</string> diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml index 9e6a4b72ac63..538a38cb2db4 100644 --- a/packages/SettingsLib/res/values-lo-rLA/strings.xml +++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ເຮັດໃຫ້ທຸກແອັບມີສິດໄດ້ຮັບການຂຽນໃສ່ບ່ອນຈັດເກັບພາຍນອກ, ໂດຍບໍ່ຄຳນຶງເຖິງຄ່າທີ່ຈະແຈ້ງ"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"ບັງຄັງໃຫ້ກິດຈະກຳປ່ຽນຂະໜາດໄດ້"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ເຮັດໃຫ້ທຸກກິດຈະກຳປ່ຽນຂະໜາດໄດ້ສຳລັບຫຼາຍໜ້າຕ່າງ, ໂດຍບໍ່ຄຳນຶງເຖິງຄ່າທີ່ຈະແຈ້ງ."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ລະຫັດຜ່ານການສຳຮອງຂໍ້ມູນເດັກສະທັອບ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ການສຳຮອງຂໍ້ມູນເຕັມຮູບແບບໃນເດັກສະທັອບຍັງບໍ່ໄດ້ຮັບການປ້ອງກັນໃນເວລານີ້"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ແຕະເພື່ອປ່ຽນ ຫຼືລຶບລະຫັດຂອງການສຳຮອງຂໍ້ມູນເຕັມຮູບແບບໃນເດັກສະທັອບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 201964647d85..014a6fb37054 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Vis. pr. gal. įr. į vid. saug. nepais. apr. vert."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Priv. nust., kad veiksm. b. g. atl. kelių d. lang."</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Nustatoma, kad visus veiksmus būtų galima atlikti kelių dydžių languose, nepaisant aprašo verčių."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Viet. atsrg. kop. slapt."</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Šiuo metu visos vietinės atsarginės kopijos neapsaugotos"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Jei norite pakeisti ar pašalinti visų vietinių atsarginių kopijų slaptažodį, palieskite"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 2f9a83b38874..87da10551316 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ļauj jebkuru lietotni ierakstīt ārējā krātuvē neatkarīgi no manifesta vērtības."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Pielāgot darbības"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pielāgo visas darbības vairāku logu režīmam neatkarīgi no vērtībām manifestā."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Datora dublējuma parole"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Darbvirsmas pilnie dublējumi pašlaik nav aizsargāti."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pieskarieties, lai mainītu vai noņemtu paroli pilniem darbvirsmas dublējumiem."</string> diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml index 51c256817024..b588c2c986b9 100644 --- a/packages/SettingsLib/res/values-mk-rMK/strings.xml +++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Запишува апл. во надв.меморија, незав. од манифест"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Принуди ги активностите да ја менуваат големината"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Ги прави сите активности да бидат со променлива големина за мултипрозорец, без разлика на вредностите на манифестот."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Овозможи прозорци со слободна форма"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Овозможува поддршка за експериментални прозорци со слободна форма."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Резервна лозинка за работна површина"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Целосни резервни копии на работната површина кои во моментов не се заштитени"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Допрете за да се промени или отстрани лозинката за целосна резервна копија на работната површина"</string> diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml index f00103f10a11..d76e87f7a562 100644 --- a/packages/SettingsLib/res/values-ml-rIN/strings.xml +++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, ബാഹ്യ സ്റ്റോറേജിലേക്ക് എഴുതപ്പെടുന്നതിന് ഏതൊരു ആപ്പിനെയും യോഗ്യമാക്കുന്നു"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"വലിപ്പം മാറ്റാൻ പ്രവർത്തനങ്ങളെ നിർബന്ധിക്കുക"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, എല്ലാ പ്രവർത്തനങ്ങളെയും മൾട്ടി-വിൻഡോയ്ക്കായി വലിപ്പം മാറ്റുന്നു."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ഡെസ്ക്ടോപ്പ് ബാക്കപ്പ് പാസ്വേഡ്"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ഡെസ്ക്ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾ നിലവിൽ പരിരക്ഷിച്ചിട്ടില്ല"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ഡെസ്ക്ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾക്കായി പാസ്വേഡുകൾ മാറ്റാനോ നീക്കംചെയ്യാനോ സ്പർശിക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml index cc0e7cb89b46..4aea631972fe 100644 --- a/packages/SettingsLib/res/values-mn-rMN/strings.xml +++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест утгыг нь үл хамааран дурын апп-ыг гадаад санах ойд бичих боломжтой болгодог"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Үйл ажиллагааны хэмжээг өөрчилж болохуйц болгох"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Тодорхойлогч файлын утгыг үл хамааран, бүх үйл ажиллагааг олон цонхонд хэмжээг нь өөрчилж болохуйц болгох."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Десктоп нөөшлөлтийн нууц үг"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Десктоп бүрэн нөөцлөлт одоогоор хамгаалалтгүй байна"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Десктоп дээрх бүрэн нөөшлөлтийн нууц үгийг өөрчлөх буюу арилгахын тулд хүрнэ үү"</string> diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml index b939ca0c6bf4..ed57fb73e530 100644 --- a/packages/SettingsLib/res/values-mr-rIN/strings.xml +++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"मॅनिफेस्ट मूल्यांकडे दुर्लक्ष करून, कोणत्याही अॅपला बाह्य संचयनावर लेखन केले जाण्यासाठी पात्र बनविते"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"क्रियाकलापाचा आकार बदलण्यायोग्य होण्याची सक्ती करा"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"मॅनिफेस्ट मूल्यांकडे दुर्लक्ष करून, एकाधिक-विंडोसाठी सर्व क्रियाकलापांचा आकार बदलण्यायोग्य करा"</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"freeform विंडो सक्षम करा"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रायोगिक मुक्तस्वरूपाच्या विंडोसाठी समर्थन सक्षम करते."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटॉप बॅकअप संकेतशब्द"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप पूर्ण बॅक अप सध्या संरक्षित नाहीत"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटॉपच्या पूर्ण बॅकअपसाठी असलेला संकेतशब्द बदलण्यासाठी किंवा काढून टाकण्यासाठी स्पर्श करा"</string> diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml index 2e6775280dcf..1f72d04297fe 100644 --- a/packages/SettingsLib/res/values-ms-rMY/strings.xml +++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Menjadikan sebarang apl layak ditulis ke storan luaran, walau apa juga nilai manifesnya"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktiviti supaya boleh diubah saiz"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Menjadikan semua aktiviti boleh diubah saiz untuk berbilang tetingkap, tanpa mengambil kira nilai manifes."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Kata laluan sandaran komputer meja"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sandaran penuh komputer meja tidak dilindungi pada masa ini"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh untuk menukar atau mengalih keluar kata laluan untuk sandaran penuh komputer meja"</string> diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml index 002aed163356..cd4812fc32f8 100644 --- a/packages/SettingsLib/res/values-my-rMM/strings.xml +++ b/packages/SettingsLib/res/values-my-rMM/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ပြနေတဲ့ တန်ဖိုး ဘယ်လိုပဲရှိနေနေ၊ ဘယ် appကို မဆို အပြင် သိုလှောင်ခန်းသို့ ရေးသားခွင့် ပေးတယ်"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"လုပ်ဆောင်ချက်များ ဆိုက်ညှိရနိုင်ရန် လုပ်ခိုင်းပါ"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"မန်နီးဖက်စ် တန်ဖိုးမရွေး၊ လုပ်ဆောင်ချက် အားလုံး ဆိုက်ညှိရနိုင်အောင် လုပ်ပေးပါ။"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop အရန်စကားဝှက်"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"အလုပ်ခုံတွင် အရန်သိမ်းဆည်းခြင်းများကို လောလောဆယ် မကာကွယ်နိုင်ပါ။"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"အလုပ်ခုံ တွင် အရန်သိမ်းဆည်းခြင်းအပြည့်လုပ်ရန် အတွက် စကားဝှက်ဖယ်ရန် သို့ ပြောင်းရန် တို့ထိပါ။"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index a496a21b9b65..ae0b15de635b 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gjør at apper kan skrives til ekstern lagring, uavhengig av manifestverdier"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til å kunne endre størrelse"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Dette gjør at alle aktivitene kan endre størrelse for flervindusmodus, uavhengig av manifestverdier."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Passord for sikkerhetskopiering på datamaskin"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Fullstendig sikkerhetskopiering på datamaskin beskyttes ikke for øyeblikket."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Trykk for å endre eller fjerne passordet for fullstendige sikkerhetskopier på datamaskinen"</string> diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml index 5c85c256c318..58855132d33e 100644 --- a/packages/SettingsLib/res/values-ne-rNP/strings.xml +++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"म्यानिफेेस्टको उपेक्षा गरी, कुनै पनि अनुप्रयोगलाई बाह्य भण्डारणमा लेख्न योग्य बनाउँछ"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"गतिविधिहरू रिसाइज गर्नको लागि बाध्य गर्नुहोस्"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"म्यानिफेेस्ट मानहरूको ख्याल नगरी, बहु-विन्डोको लागि सबै रिसाइज गर्न सकिने गतिविधिहरू बनाउँछ।"</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"फ्रीफर्म विन्डोहरू सक्रिय गर्नुहोस्"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रयोगात्मक फ्रीफर्म विन्डोहरूका लागि समर्थनलाई सक्रिय गर्छ।"</string> <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटप ब्याकअप पासवर्ड"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटप पूर्ण जगेडाहरू हाललाई सुरक्षित छैनन्"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटप पूर्ण ब्याकअपको लागि पासवर्ड बदल्न वा हटाउन छुनुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 1afa4441b492..1dd477dd7f92 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Hierdoor komt een app in aanmerking om te worden geschreven naar externe opslag, ongeacht de manifestwaarden"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Formaat activiteiten geforceerd aanpasbaar maken"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Hiermee wordt het formaat van alle activiteiten aanpasbaar gemaakt, ongeacht de manifestwaarden."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Vensters met vrije vorm inschakelen"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Schakelt ondersteuning in voor vensters met experimentele vrije vorm."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Wachtwoord desktopback-up"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Volledige back-ups naar desktops zijn momenteel niet beveiligd"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Raak dit aan om het wachtwoord voor volledige back-ups naar desktops te wijzigen of te verwijderen"</string> diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml index ad3d3ede4c15..85f9ffb284f4 100644 --- a/packages/SettingsLib/res/values-pa-rIN/strings.xml +++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ਇੱਕ ਐਪ ਨੂੰ ਬਾਹਰਲੀ ਸਟੋਰੇਜ ਤੇ ਲਿਖਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ, ਮੈਨੀਫੈਸਟ ਵੈਲਯੂਜ ਤੇ ਵਿਚਾਰ ਕੀਤੇ ਬਿਨਾਂ"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"ਮੁੜ-ਆਕਾਰ ਬਦਲਣ ਲਈ ਸਰਗਰਮੀਆਂ \'ਤੇ ਜ਼ੋਰ ਦਿਓ"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ਮਲਟੀ-ਵਿੰਡੋ ਲਈ ਸਾਰੀਆਂ ਸਰਗਰਮੀਆਂ ਨੂੰ ਮੁੜ-ਆਕਾਰ ਵਿੱਚ ਲਿਆਉਂਦੀ ਹੈ, ਚਾਹੇ ਮੈਨੀਫੈਸਟ ਵੈਲਯੂਜ਼ ਕੁਝ ਵੀ ਹੋਣ।"</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"freeform windows ਨੂੰ ਯੋਗ ਬਣਾਓ"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ਪ੍ਰਯੋਗਾਤਮਕ freeform windows ਲਈ ਸਮਰਥਨ ਨੂੰ ਯੋਗ ਬਣਾਉਂਦੀ ਹੈ।"</string> <string name="local_backup_password_title" msgid="3860471654439418822">"ਡੈਸਕਟੌਪ ਬੈਕਅਪ ਪਾਸਵਰਡ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ਡੈਸਕਟੌਪ ਪੂਰੇ ਬੈਕਅਪਸ ਇਸ ਵੇਲੇ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹਨ"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ਡੈਸਕਟੌਪ ਪੂਰੇ ਬੈਕਅਪਸ ਲਈ ਪਾਸਵਰਡ ਬਦਲਣ ਜਾਂ ਹਟਾਉਣ ਲਈ ਛੋਹਵੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index d58070ee33eb..9f7c022de110 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Pozwala na zapis aplikacji w pamięci zewn. niezależnie od wartości w pliku manifestu"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Wymuś zmianę rozmiaru okien aktywności"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Umożliwia zmianę rozmiaru wszystkich okien aktywności w trybie wielu okien niezależnie od ustawień w pliku manifestu."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Włącz dowolny rozmiar okien"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Włącza obsługę eksperymentalnej funkcji dowolnego rozmiaru okien."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Hasło kopii zapasowej"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Pełne kopie zapasowe na komputerze nie są obecnie chronione"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Wybierz, aby zmienić lub usunąć hasło pełnych kopii zapasowych na komputerze stacjonarnym."</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 3b280431ba78..dd4b58d51cbc 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index c5f94cb486b4..0cbe5e4e7f86 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualquer aplic. pode ser gravada no arm. ext., independ. dos valores do manif."</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar as atividades a serem redimensionáveis"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Palavra-passe cópia do comp."</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"As cópias de segurança completas no ambiente de trabalho não estão atualmente protegidas"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a palavra-passe para cópias de segurança completas no ambiente de trabalho"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 3b280431ba78..dd4b58d51cbc 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index c76faf02e19c..a666da042bfb 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Face orice aplicație eligibilă să fie scrisă în stocarea externă, indiferent de valorile manifestului"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forțați redimensionarea activităților"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Parolă copie rez. desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"În prezent, copiile de rezervă complete pe desktop nu sunt protejate"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Atingeţi pentru a modifica sau pentru a elimina parola pentru copiile de rezervă complete pe desktop"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index f1cd18254dbf..e93bae13bc98 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Разрешает сохранение приложений на внешние накопители независимо от значения манифеста"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Изменение размера в многооконном режиме"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Позволяет менять размер в многооконном режиме (независимо от значений манифеста)"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Пароль для резервного копирования"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Полные резервные копии в настоящее время не защищены"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Изменить или удалить пароль для резервного копирования"</string> diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml index eb13ce498437..6753624a9bba 100644 --- a/packages/SettingsLib/res/values-si-rLK/strings.xml +++ b/packages/SettingsLib/res/values-si-rLK/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"මැනිෆෙස්ට් අගයන් නොසලකා, ඕනෑම යෙදුමක් අභ්යන්තර ගබඩාවට ලිවීමට සුදුසුකම් ලබා දෙයි"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"ක්රියාකාරකම් ප්රතිප්රමාණ කළ හැකි බවට බල කරන්න"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"මැනිෆෙස්ට් අගයන් නොසලකා, සියලු ක්රියාකාරකම් බහු-කවුළු සඳහා ප්රතිප්රමාණ කළ හැකි බවට පත් කරයි."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ඩෙස්ක්ටොප් උපස්ථ මුරපදය"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ දැනට ආරක්ෂා කර නොමැත"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ සඳහා මුරපදය වෙනස් කිරීමට හෝ ඉවත් කිරීමට ස්පර්ශ කරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index b5b6d2a2b303..7db7835eda8e 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Umožňuje zapísať akúkoľvek aplikáciu do externého úložiska bez ohľadu na hodnoty v manifeste"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Vynútiť možnosť zmeny veľkosti aktivít"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veľkosti všetkých aktivít bude možné zmeniť na niekoľko okien (bez ohľadu na hodnoty manifestu)."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Heslo pre zálohy v počítači"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Úplné zálohy na počítači nie sú momentálne chránené"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotykom zmeníte alebo odstránite heslo pre úplné zálohy do počítača"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index cb107d7e58a9..ed37933daff3 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsako aplikacijo zapisati v zunanjo shrambo"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Vsili povečanje velikosti za aktivnosti"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsem aktivnostim povečati velikost za način z več okni."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Geslo za varn. kop. rač."</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Popolne varnostne kopije namizja trenutno niso zaščitene"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotaknite se, če želite spremeniti ali odstraniti geslo za popolno varnostno kopiranje namizja."</string> diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml index c176f18aa579..7f4d6a864af9 100644 --- a/packages/SettingsLib/res/values-sq-rAL/strings.xml +++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Bën që çdo aplikacion të jetë i përshtatshëm për t\'u shkruar në hapësirën ruajtëse të jashtme, pavarësisht nga vlerat e manifestit"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Detyro madhësinë e ndryshueshme për aktivitetet"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Bën që të gjitha aktivitetet të kenë madhësi të ndryshueshme për përdorimin me shumë dritare, pavarësisht vlerave të manifestit."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivizo dritaret me formë të lirë"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktivizon mbështetjen për dritaret eksperimentale me formë të lirë."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Fjalëkalimi rezervë i kompjuterit"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervimet e plota në kompjuter nuk janë të mbrojtura aktualisht"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Prek për të ndryshuar ose hequr fjalëkalimin për rezervime të plota të desktopit"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 6b6609493d55..9596ce5e1caf 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Омогућава уписивање свих апликација у спољну меморију, без обзира на вредности манифеста"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Принудно омогући промену величине активности"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Омогућава промену величине свих активности за режим са више прозора, без обзира на вредности манифеста."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Лозинка резервне копије за рачунар"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Резервне копије читавог система тренутно нису заштићене"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Додирните да бисте променили или уклонили лозинку за прављење резервних копија читавог система на рачунару"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 40e1593250be..7d0d0a08b0e9 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Appen kan skrivas till extern lagring, oavsett manifestvärden"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Framtvinga storleksanpassning för aktiviteter"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Detta gör det möjligt att ändra storleken på alla aktiviteter i flerfönsterläge, oavsett manifestvärden."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Lösenord för säkerhetskopia av datorn"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"De fullständiga säkerhetskopiorna av datorn är för närvarande inte skyddade"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tryck om du vill ändra eller ta bort lösenordet för fullständig säkerhetskopiering av datorn"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index a014e1d43536..d1041da0db20 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Huweka programu kwenye hifadhi ya nje, bila kujali maelezo"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Lazimisha shughuli ziweze kubadilishwa ukubwa"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Fanya shughuli zote ziweze kubadilishwa ukubwa kwa ajili ya dirisha nyingi, bila kujali thamani za faili ya maelezo."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string> diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml index 4f6ea57d6a48..e66f07325677 100644 --- a/packages/SettingsLib/res/values-ta-rIN/strings.xml +++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"மேனிஃபெஸ்ட் மதிப்புகளை பொருட்படுத்தாமல், எந்தப் பயன்பாட்டையும் வெளிப்புற சேமிப்பிடத்தில் எழுத அனுமதிக்கும்"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"செயல்பாடுகளை அளவுமாறக்கூடியதாக அமை"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"மேனிஃபெஸ்ட் மதிப்புகளைப் பொருட்படுத்தாமல், பல சாளரத்திற்கு எல்லா செயல்பாடுகளையும் அளவுமாறக்கூடியதாக அமைக்கும்."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"டெஸ்க்டாப் காப்புப்பிரதி கடவுச்சொல்"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"டெஸ்க்டாப்பின் முழு காப்புப்பிரதிகள் தற்போது பாதுகாக்கப்படவில்லை"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"டெஸ்க்டாப்பின் முழுமையான காப்புப்பிரதிகளுக்கான கடவுச்சொல்லை மாற்றுவதற்கு அல்லது அகற்றுவதற்குத் தொடவும்"</string> diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml index a6d2140313e8..80b84f06cc09 100644 --- a/packages/SettingsLib/res/values-te-rIN/strings.xml +++ b/packages/SettingsLib/res/values-te-rIN/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ఏ అనువర్తనాన్ని అయినా మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా బాహ్య నిల్వలో వ్రాయగలిగేలా అనుమతిస్తుంది"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"కార్యాచరణలను పరిమాణం మార్చగలిగేలా నిర్బంధించు"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా అన్ని కార్యాచరణలను బహుళ విండోల్లో సరిపోయేటట్లు పరిమాణం మార్చగలిగేలా చేస్తుంది."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"స్వతంత్ర రూప విండోలను ప్రారంభించండి"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ప్రయోగాత్మక స్వతంత్ర రూప విండోలకు మద్దతును ప్రారంభిస్తుంది."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"డెస్క్టాప్ బ్యాకప్ పాస్వర్డ్"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"డెస్క్టాప్ పూర్తి బ్యాకప్లు ప్రస్తుతం రక్షించబడలేదు"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"డెస్క్టాప్ పూర్తి బ్యాకప్ల కోసం పాస్వర్డ్ను మార్చడానికి లేదా తీసివేయడానికి తాకండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index de2f33dbe377..6826419ce278 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ให้สามารถเขียนแอปต่างๆ ไปยังที่เก็บภายนอกได้ โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"บังคับให้กิจกรรมปรับขนาดได้"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ทำให้กิจกรรมทั้งหมดปรับขนาดได้สำหรับหน้าต่างหลายบาน โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"รหัสผ่านการสำรองข้อมูลในเดสก์ท็อป"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"การสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป ไม่ได้รับการป้องกันในขณะนี้"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"แตะเพื่อเปลี่ยนหรือลบรหัสผ่านสำหรับการสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 231079b92e7b..8f623268e23e 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mara-write na sa external storage ang anumang app, anuman ang manifest value"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Sapilitang gawing resizable ang mga aktibidad"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gawing resizable para sa multi-window ang lahat ng aktibidad, anuman ang mga manifest value."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Password ng pag-backup ng desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kasalukuyang hindi pinoprotektahan ang mga buong pag-backup ng desktop"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pindutin upang baguhin o alisin ang password para sa mga buong pag-backup ng desktop"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index c44d7f8ee574..5e0839a6db10 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Bildirilen değerlerden bağımsız olarak uygulamaları harici depolamaya yazmak için uygun hale getirir"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Etkinlikleri yeniden boyutlandırılabilmeye zorla"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest değerlerinden bağımsız olarak, tüm etkinlikleri birden fazla pencerede yeniden boyutlandırılabilir hale getirir."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Masaüstü yedekleme şifresi"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Masaüstü tam yedeklemeleri şu an korunmuyor"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Masaüstü tam yedeklemelerinin şifresini değiştirmek veya kaldırmak için dokunun"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index d5dbe902aaa4..2448b2326ae7 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Додатки можна записувати на зовнішню пам’ять незалежно від значень маніфесту"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Примусово масштабувати активність"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Активність масштабуватиметься на кілька вікон, незалежно від значень у файлі маніфесту."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Увімкнути вікна довільного формату"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Увімкнуться експериментальні вікна довільного формату."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Пароль резерв.копії на ПК"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Повні резервні копії на комп’ютері наразі не захищені"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Торкніться, щоб змінити чи видалити пароль для повного резервного копіювання на комп’ютер"</string> diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml index 4c9c17850409..0834303fb310 100644 --- a/packages/SettingsLib/res/values-ur-rPK/strings.xml +++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"manifest اقدار سے قطع نظر، کسی بھی ایپ کو بیرونی اسٹوریج پر لکھے جانے کا اہل بناتا ہے"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"سرگرمیوں کو ری سائز ایبل بنائیں"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"manifest اقدار سے قطع نظر، ملٹی ونڈو کیلئے تمام سرگرمیوں کو ری سائز ایبل بناتا ہے۔"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"ڈیسک ٹاپ کا بیک اپ پاس ورڈ"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ڈیسک ٹاپ کے مکمل بیک اپس فی الحال محفوظ کیے ہوئے نہیں ہیں"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ڈیسک ٹاپ کے مکمل بیک اپس کیلئے پاس ورڈ کو تبدیل کرنے یا ہٹانے کیلئے ٹچ کریں"</string> diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml index d138a284b81d..d2d8b76670dc 100644 --- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml +++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Manifest qiymatidan qat’i nazar istalgan ilovani tashqi xotiraga saqlash imkonini beradi"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Harakatlarni moslashuvchan o‘lchamga keltirish"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest qiymatidan qat’i nazar barcha harakatlarni ko‘p oynali rejimga moslashtiradi."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Zaxira nusxa uchun parol"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kompyuterdagi zaxira nusxalar hozirgi vaqtda himoyalanmagan"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ish stoli to\'liq zaxira nusxalari parolini o‘zgartirish yoki o‘chirish uchun bu yerni bosing."</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 7ab8b0226226..178e301636cc 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Giúp ứng dụng bất kỳ đủ điều kiện được ghi vào bộ nhớ ngoài bất kể giá trị tệp kê khai là gì"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Buộc các hoạt động có thể thay đổi kích thước"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Giúp tất cả hoạt động có thể thay đổi kích thước cho nhiều cửa sổ bất kể giá trị tệp kê khai là gì."</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"Mật khẩu sao lưu của máy tính"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sao lưu toàn bộ máy tính hiện không được bảo vệ"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Chạm để thay đổi hoặc xóa mật khẩu dành cho bộ sao lưu toàn bộ tới máy tính"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 509ff88de7d1..5b963832a61a 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允许将任何应用写入外部存储设备(无论清单值是什么)"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"强制将活动设为可调整大小"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"将所有活动设为可配合多窗口环境调整大小(无论清单值是什么)。"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"桌面备份密码"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌面完整备份当前未设置密码保护"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"触摸可更改或删除用于桌面完整备份的密码"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index f323667d67c3..db5f09b42068 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將所有應用程式寫入到外部儲存完間 (所有資訊清單值)"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"強制可變更活動尺寸"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"在任何資訊清單值下,允許為多個視窗變更所有活動的尺寸。"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"桌面電腦備份密碼"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌上電腦的完整備份目前未受保護"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可更改或移除桌上電腦完整備份的密碼"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index c6ad832ca826..787a44218917 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -247,6 +247,10 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"將所有活動設為可配合多重視窗環境調整大小 (無論資訊清單值為何)。"</string> + <!-- no translation found for enable_freeform_support (1461893351278940416) --> + <skip /> + <!-- no translation found for enable_freeform_support_summary (2252563497485436534) --> + <skip /> <string name="local_backup_password_title" msgid="3860471654439418822">"電腦備份密碼"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"電腦完整備份目前未受保護"</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可變更或移除電腦完整備份的密碼"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index cb3670f06863..bf3addfa3327 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -247,6 +247,8 @@ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Yenza noma uluphi uhlelo lokusebenza lifaneleke ukuthi libhalwe kusitoreji sangaphandle, ngaphandle kwamavelu we-manifest"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Imisebenzi yamandla izonikezwa usayizi omusha"</string> <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Yenza yonke imisebenzi ibe nosayizi abasha kuwindi lokuningi, ngokunganaki amanani we-manifest."</string> + <string name="enable_freeform_support" msgid="1461893351278940416">"Nika amandla amawindi e-freeform"</string> + <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Inika amandla usekelo lwamawindi okuhlola e-freeform."</string> <string name="local_backup_password_title" msgid="3860471654439418822">"Iphasiwedi yokusekela ngokulondoloza ye-Desktop"</string> <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Ukusekela ngokulondoloza okugcwele kwe-Desktop akuvikelekile okwamanje."</string> <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Khetha ukushintsha noma ukususa iphasiwedi yokwenziwa kwezipele ngokugcwele kwideskithophu"</string> diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 2a4efbd9abcd..1bce7f9ac3ae 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -128,7 +128,7 @@ <item>16777216</item> </string-array> - <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=30]--> + <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]--> <string-array name="select_logd_size_summaries" > <item>Off</item> <item>64K per log buffer</item> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 7e2288133ab3..f7e25dbe5f6e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -624,6 +624,11 @@ <!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] --> <string name="force_resizable_activities_summary">Makes all activities resizable for multi-window, regardless of manifest values.</string> + <!-- UI debug setting: enable freeform window support [CHAR LIMIT=50] --> + <string name="enable_freeform_support">Enable freeform windows</string> + <!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] --> + <string name="enable_freeform_support_summary">Enables support for experimental freeform windows.</string> + <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] --> <string name="local_backup_password_title">Desktop backup password</string> <!-- Summary text of the "local backup password" setting when the user has not supplied a password --> diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png Binary files differnew file mode 100644 index 000000000000..f3be2ee1ecb0 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml index 22ed216de570..7ea5027b78f4 100644 --- a/packages/SystemUI/res/layout/docked_stack_divider.xml +++ b/packages/SystemUI/res/layout/docked_stack_divider.xml @@ -24,10 +24,9 @@ android:id="@+id/docked_divider_background" android:background="@color/docked_divider_background"/> - <ImageButton + <com.android.systemui.stackdivider.DividerHandleView style="@style/DockedDividerHandle" android:id="@+id/docked_divider_handle" - android:background="@null" - android:src="@drawable/docked_divider_handle"/> + android:background="@null"/> </com.android.systemui.stackdivider.DividerView> diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 3cdee640dd26..071b7c8a6da8 100644 --- a/packages/SystemUI/res/layout/notification_guts.xml +++ b/packages/SystemUI/res/layout/notification_guts.xml @@ -29,25 +29,22 @@ android:background="@color/notification_guts_text_color" > <!-- header --> - <FrameLayout + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingBottom="8dp" > + android:paddingBottom="8dp" + android:paddingTop="8dp" + android:id="@+id/notification_guts_header" + android:orientation="horizontal" + android:layout_gravity="center_vertical|start"> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/notification_guts_header" - android:orientation="horizontal" - android:layout_gravity="center_vertical|start" - android:layout_marginEnd="52dp"> - <ImageView - android:id="@android:id/icon" - android:layout_width="18dp" - android:layout_height="18dp" - android:layout_marginEnd="3dp" - android:src="@android:drawable/arrow_down_float" /> - <TextView + <ImageView + android:id="@android:id/icon" + android:layout_width="18dp" + android:layout_height="18dp" + android:layout_marginEnd="3dp" + android:src="@android:drawable/arrow_down_float" /> + <TextView android:id="@+id/pkgname" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -55,7 +52,7 @@ android:layout_marginStart="3dp" android:layout_marginEnd="4dp" android:textColor="@color/notification_guts_title_color" /> - <TextView + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/debug_info" @@ -64,18 +61,7 @@ android:layout_gravity="bottom|start" android:visibility="gone" android:textColor="#ffffff" /> - </LinearLayout> - - <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small" - android:id="@+id/notification_inspect_item" - android:layout_width="52dp" - android:layout_height="wrap_content" - android:layout_weight="0" - android:gravity="center" - android:layout_gravity="center_vertical|end" - android:contentDescription="@string/status_bar_notification_inspect_item_title" - android:src="@drawable/ic_settings" /> - </FrameLayout> + </LinearLayout> <!-- Importance slider --> <LinearLayout android:layout_width="match_parent" @@ -157,4 +143,34 @@ android:visibility="gone"/> </RadioGroup> </LinearLayout> + <!-- buttons --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="end" + android:paddingTop="8dp" + android:paddingBottom="16dp" > + + <TextView + android:id="@+id/more_settings" + android:text="@string/notification_more_settings" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.NotificationGuts" + android:background="@drawable/btn_borderless_rect" + android:gravity="center" + android:paddingEnd="24dp" + android:paddingStart="12dp" + android:focusable="true" /> + + <TextView + android:id="@+id/done" + android:text="@string/notification_done" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.NotificationGuts" + android:background="@drawable/btn_borderless_rect" + android:gravity="center" + android:focusable="true"/> + </LinearLayout> </com.android.systemui.statusbar.NotificationGuts> diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index 2b82b05cab86..5b44c178f872 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -34,9 +34,10 @@ android:layout_gravity="bottom|right" android:layout_marginRight="15dp" android:layout_marginBottom="15dp" - android:translationZ="2dp" + android:translationZ="4dp" android:contentDescription="@string/recents_lock_to_app_button_label" - android:background="@drawable/recents_lock_to_task_button_bg"> + android:background="@drawable/recents_lock_to_task_button_bg" + android:visibility="invisible"> <ImageView android:layout_width="@dimen/recents_lock_to_app_icon_size" android:layout_height="@dimen/recents_lock_to_app_icon_size" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index f81bf23dec47..01879935ee6b 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Wys hierdie kennisgewings sonder geluide"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Wys boaan die kennisgewinglys en maak \'n geluid"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Verskyn vlugtig op die skerm en maak \'n geluid"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string> + <string name="notification_done" msgid="5279426047273930175">"Klaar"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normale kleure"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Aandkleure"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Gepasmaakte kleure"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index d96e7906e032..503a76709648 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"እነዚህን ማሳወቂያዎች በጸጥታ አሳይ"</string> <string name="notification_importance_high" msgid="3222680136612408223">"በማሳወቂያዎች ዝርዝር ላይኛው ክፍል ላይ አሳይና ድምፅ አሰማ"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ወደ ገጸ ማያው ይመልከቱና ድምፅ ይቅረጹ"</string> + <string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string> + <string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string> <string name="color_matrix_none" msgid="2121957926040543148">"መደበኛ ቀለሞች"</string> <string name="color_matrix_night" msgid="5943817622105307072">"የለሊት ቀለሞች"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ብጁ ቀለሞች"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b3e494c72ead..d89d0362be2d 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -464,6 +464,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"عرض هذه الإشعارات بدون تنبيه صوتي"</string> <string name="notification_importance_high" msgid="3222680136612408223">"العرض أعلى قائمة الإشعارات مع تنبيه صوتي"</string> <string name="notification_importance_max" msgid="5236987171904756134">"الظهور سريعًا على الشاشة مع تنبيه صوتي"</string> + <string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string> + <string name="notification_done" msgid="5279426047273930175">"تم"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ألوان عادية"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ألوان ليلية"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ألوان مخصصة"</string> diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml index 4efc5df78f4c..55f08419dca6 100644 --- a/packages/SystemUI/res/values-az-rAZ/strings.xml +++ b/packages/SystemUI/res/values-az-rAZ/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildişləri səssiz göstərin"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Bildirişlər siyahısında yuxarıda göstərin və səsli edin"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Ekranda nəzər salın və səsli edin"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string> + <string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normal rənglər"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Gecə rəngləri"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Xüsusi rənglər"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index f482e9666dcc..4056963c8e6a 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -455,12 +455,14 @@ <string name="low_importance" msgid="4109929986107147930">"Mala važnost"</string> <string name="default_importance" msgid="8192107689995742653">"Uobičajena važnost"</string> <string name="high_importance" msgid="1527066195614050263">"Velika važnost"</string> - <string name="max_importance" msgid="5089005872719563894">"Najveća važnost"</string> + <string name="max_importance" msgid="5089005872719563894">"Važnost: hitno"</string> <string name="notification_importance_blocked" msgid="2397192642657872872">"Ova obaveštenja se nikada ne prikazuju"</string> <string name="notification_importance_low" msgid="4383563267370859725">"Prikazuju se u dnu liste obaveštenja bez zvuka"</string> <string name="notification_importance_default" msgid="4926529615920610817">"Ova obaveštenja se prikazuju bez zvuka"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Prikazuju se u vrhu liste obaveštenja i emituje se zvuk"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Nakratko se prikazuju na ekranu i emituje se zvuk"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string> + <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normalne boje"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Noćne boje"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Prilagođene boje"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 95c4a2243ba4..93c26a4dacfa 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Тези известия се показват без звук"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Показване най-горе в списъка с известия и издаване на звуков сигнал"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Показване на екрана и издаване на звуков сигнал"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string> + <string name="notification_done" msgid="5279426047273930175">"Готово"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Нормални цветове"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Нощни цветове"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Персонализирани цветове"</string> diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml index 38885d56678d..939465b64d29 100644 --- a/packages/SystemUI/res/values-bn-rBD/strings.xml +++ b/packages/SystemUI/res/values-bn-rBD/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"নিঃশব্দে এই বিজ্ঞপ্তিগুলি দেখানো হয়"</string> <string name="notification_importance_high" msgid="3222680136612408223">"বিজ্ঞপ্তি তালিকার শীর্ষে দেখানো হয় এবং শব্দ করে"</string> <string name="notification_importance_max" msgid="5236987171904756134">"স্ক্রীনের উপরে দেখানো হয় এবং শব্দ করে"</string> + <string name="notification_more_settings" msgid="816306283396553571">"আরো সেটিংস"</string> + <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string> <string name="color_matrix_none" msgid="2121957926040543148">"স্বাভাবিক রঙ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"রাতের রঙ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"কাস্টম রঙ"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index d27c3ee31d99..e19aa0efe6e2 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostra aquestes notificacions de manera silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostra a la part superior de la llista de notificacions i emet un so"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostra a la pantalla i emet un so"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string> + <string name="notification_done" msgid="5279426047273930175">"Fet"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Colors normals"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Colors nocturns"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Colors personalitzats"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4f26dc118225..b1cd561f63c8 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tato oznámení zobrazovat bez zvukového upozornění"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Tato oznámení zobrazovat na začátku seznamu a upozornit na ně zvukem"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Tato oznámení zobrazovat přímo na obrazovce a upozornit na ně zvukem"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string> + <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normální barvy"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Noční barvy"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Vlastní barvy"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 3f868a89d305..2100c57a2b48 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Vis disse underretninger lydløst"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Vis øverst på listen over underretninger, og giv lyd"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Vis på skærmen, og giv lyd"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string> + <string name="notification_done" msgid="5279426047273930175">"Færdig"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Almindelige farver"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nattefarver"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Tilpassede farver"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 4c87758b10a3..1ad5b26eb543 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Diese Benachrichtigungen ohne Ton anzeigen"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mit Ton ganz oben in der Benachrichtigungsliste anzeigen"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mit Ton auf dem Display einblenden"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string> + <string name="notification_done" msgid="5279426047273930175">"Fertig"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Standardfarben"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nachtfarben"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Benutzerdefinierte Farben"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 21e1eeec60fa..f56d63016128 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Να εμφανίζονται αυτές οι ειδοποιήσεις χωρίς ήχο"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Να εμφανίζονται στην κορυφή της λίστας ειδοποιήσεων με ήχο"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Να προβάλλονται στην οθόνη και να συνοδεύονται από κάποιον ήχο"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string> + <string name="notification_done" msgid="5279426047273930175">"Τέλος"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Κανονικά χρώματα"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Νυχτερινά χρώματα"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Προσαρμοσμένα χρώματα"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index fa8b6c3312ee..f6fc9976d4fb 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string> + <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> + <string name="notification_done" msgid="5279426047273930175">"Finished"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index fa8b6c3312ee..f6fc9976d4fb 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string> + <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> + <string name="notification_done" msgid="5279426047273930175">"Finished"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index fa8b6c3312ee..f6fc9976d4fb 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string> + <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> + <string name="notification_done" msgid="5279426047273930175">"Finished"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 54850f0816f7..fd59418c3048 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificaciones de manera silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar en la parte superior de la lista de notificaciones y emitir sonido"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar en la pantalla y emitir sonido"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string> + <string name="notification_done" msgid="5279426047273930175">"Listo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Colores normales"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Colores nocturnos"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Colores personalizados"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 41d83dc8fdff..223e684e3a0f 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificaciones de forma silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar en la parte superior de la lista de notificaciones y emitir sonido"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar en la pantalla y emitir sonido"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Más ajustes"</string> + <string name="notification_done" msgid="5279426047273930175">"Listo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Colores normales"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Colores nocturnos"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Colores personalizados"</string> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 9bb78981a4fa..f50e78cba01c 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Kuva need märguanded vaikselt"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Kuva märguannete loendi ülaosas koos heliga"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Kuva ekraani servas koos heliga"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Rohkem seadeid"</string> + <string name="notification_done" msgid="5279426047273930175">"Valmis"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Tavalised värvid"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Öised värvid"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Kohandatud värvid"</string> diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml index e62303a175db..c481f521362b 100644 --- a/packages/SystemUI/res/values-eu-rES/strings.xml +++ b/packages/SystemUI/res/values-eu-rES/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Erakutsi jakinarazpen hauek, baina soinurik egin gabe"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Erakutsi jakinarazpen hauek zerrendaren goialdean eta egin soinua"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Agerrarazi jakinarazpen hauek pantailan eta egin soinua"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string> + <string name="notification_done" msgid="5279426047273930175">"Eginda"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Kolore normalak"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Gaueko koloreak"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Kolore pertsonalizatuak"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index f45c01b8ad3f..3dee7c9be11b 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"این اعلانها بیصدا نشان داده شوند"</string> <string name="notification_importance_high" msgid="3222680136612408223">"در بالای فهرست اعلانها و به همراه صدا نشان داده شود"</string> <string name="notification_importance_max" msgid="5236987171904756134">"در جلوی صفحه به همراه صدا نشان داده شود"</string> + <string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string> + <string name="notification_done" msgid="5279426047273930175">"تمام"</string> <string name="color_matrix_none" msgid="2121957926040543148">"رنگهای عادی"</string> <string name="color_matrix_night" msgid="5943817622105307072">"رنگهای شب"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"رنگهای سفارشی"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index af1a1e57a5b4..4a5c3026bd84 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Näytä nämä ilmoitukset huomaamattomasti"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Näytä ilmoitukset luettelon kärjessä ja toista merkkiääni"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Näytä ilmoitus näytöllä ja toista äänimerkki"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string> + <string name="notification_done" msgid="5279426047273930175">"Valmis"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Tavalliset värit"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Yövärit"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Muokatut värit"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 20a8bdc1d2a8..2a4f4d9835c2 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Afficher ces notifications en mode silencieux"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Afficher en haut de la liste des notifications et émettre un son"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Afficher sur l\'écran et émettre un son"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string> + <string name="notification_done" msgid="5279426047273930175">"Terminé"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Couleurs normales"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Couleurs nocturnes"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Couleurs personnalisées"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 8e76ee8452ab..928a8b30579e 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Afficher ces notifications en mode silencieux"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Afficher en haut de la liste des notifications et émettre un son"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Afficher sur l\'écran et émettre un son"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string> + <string name="notification_done" msgid="5279426047273930175">"Terminé"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Couleurs normales"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Couleurs nocturnes"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Couleurs personnalisées"</string> diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml index 4562d89bc8c7..2a951f726808 100644 --- a/packages/SystemUI/res/values-gl-rES/strings.xml +++ b/packages/SystemUI/res/values-gl-rES/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificacións de forma silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificacións e emitir son"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar na pantalla e emitir son"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string> + <string name="notification_done" msgid="5279426047273930175">"Feito"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Cores nocturnas"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string> diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml index 69174efccee3..f331ef90cdcb 100644 --- a/packages/SystemUI/res/values-gu-rIN/strings.xml +++ b/packages/SystemUI/res/values-gu-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"આ સૂચનાઓ ચુપચાપ બતાવો"</string> <string name="notification_importance_high" msgid="3222680136612408223">"સૂચનાઓની સૂચિની ટોચ પર બતાવો અને અવાજ કરો"</string> <string name="notification_importance_max" msgid="5236987171904756134">"સ્ક્રીન પર ત્વરિત દ્રષ્ટિ કરો અને અવાજ કરો"</string> + <string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string> + <string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string> <string name="color_matrix_none" msgid="2121957926040543148">"સામાન્ય રંગો"</string> <string name="color_matrix_night" msgid="5943817622105307072">"રાત્રિ રંગો"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"કસ્ટમ રંગો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 1ab7f71b4b02..b31c1e0fd251 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ये नोटिफिकेशन मौन रूप से दिखाएं"</string> <string name="notification_importance_high" msgid="3222680136612408223">"नोटिफिकेशन सूची में सबसे ऊपर दिखाएं और ध्वनि चलाएं"</string> <string name="notification_importance_max" msgid="5236987171904756134">"स्क्रीन पर एक झलक दिखाएं और ध्वनि चलाएं"</string> + <string name="notification_more_settings" msgid="816306283396553571">"और सेटिंग"</string> + <string name="notification_done" msgid="5279426047273930175">"हो गया"</string> <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रंग"</string> <string name="color_matrix_night" msgid="5943817622105307072">"रात्रि के रंग"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"कस्टम रंग"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index a67258c3f2e0..3bef6e993024 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -461,6 +461,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Prikaži te obavijesti tiho"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Prikaži na vrhu popisa obavijesti i emitiraj zvučni signal"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Prikaži na zaslonu i emitiraj zvučni signal"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string> + <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Uobičajene boje"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Noćne boje"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Prilagođene boje"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 8589e954cebf..8dc9a193ae66 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Hang nélkül jelennek meg ezek az értesítések"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Az értesítési lista tetején jelennek meg hangjelzéssel"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Az értesítések felugranak a képernyőn hangjelzéssel"</string> + <string name="notification_more_settings" msgid="816306283396553571">"További beállítások"</string> + <string name="notification_done" msgid="5279426047273930175">"Kész"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Hagyományos színek"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Esti színek"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Egyéni színek"</string> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 1b8de045425c..99d6980ae77c 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Ցուցադրել այս ծանուցումներն առանց ձայնային ազդանշանի"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Ցուցադրել ծանուցումների ցանկի վերևում և հնչեցնել ձայնային ազդանշան"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Ցուցադրել էկրանին և հնչեցնել ձայնային ազդանշան"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string> + <string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Սովորական գույներ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Գիշերային գույներ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Հատուկ գույներ"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 9075484169fe..f6124db7f133 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tunjukkan notifikasi ini tanpa suara"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Tunjukkan di bagian atas daftar notifikasi dan bunyikan suara"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Muncul di layar dan bunyikan suara"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string> + <string name="notification_done" msgid="5279426047273930175">"Selesai"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Warna normal"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Warna malam"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Warna khusus"</string> diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml index 556018c4a143..7ea1746b40e6 100644 --- a/packages/SystemUI/res/values-is-rIS/strings.xml +++ b/packages/SystemUI/res/values-is-rIS/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Sýna þessar tilkynningar án hljóðs"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Sýna efst á tilkynningalistanum og spila hljóð"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Birta á skjánum og spila hljóð"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string> + <string name="notification_done" msgid="5279426047273930175">"Lokið"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Venjulegir litir"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Næturlitir"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Sérsniðnir litir"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index b49c3871d5b8..b395f8b2a55d 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostra silenziosamente queste notifiche"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostra nella parte superiore dell\'elenco delle notifiche e riproduci suono"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Apri sullo schermo e riproduci suono"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string> + <string name="notification_done" msgid="5279426047273930175">"Fine"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Colori normali"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Colori per la notte"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Colori personalizzati"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index e6ac5af1db40..2c894f567267 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"הצג את ההודעות האלה בלי להשמיע צליל"</string> <string name="notification_importance_high" msgid="3222680136612408223">"הצג בחלק העליון של רשימת ההודעות והשמע צליל"</string> <string name="notification_importance_max" msgid="5236987171904756134">"הצג לרגע על המסך והשמע צליל"</string> + <string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string> + <string name="notification_done" msgid="5279426047273930175">"סיום"</string> <string name="color_matrix_none" msgid="2121957926040543148">"צבעים רגילים"</string> <string name="color_matrix_night" msgid="5943817622105307072">"צבעי לילה"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"צבעים מותאמים אישית"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index e7e3a86ca5bc..580d995a4957 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"この通知をマナーモードで表示する"</string> <string name="notification_importance_high" msgid="3222680136612408223">"通知リストの先頭に表示し、音声でも知らせる"</string> <string name="notification_importance_max" msgid="5236987171904756134">"画面に数秒間表示し、音声でも知らせる"</string> + <string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string> + <string name="notification_done" msgid="5279426047273930175">"完了"</string> <string name="color_matrix_none" msgid="2121957926040543148">"標準の色"</string> <string name="color_matrix_night" msgid="5943817622105307072">"夜間の色"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"カスタムの色"</string> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 79e0f4fea84d..7a51668f09ae 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ამ შეტყობინებების უხმოდ ჩვენება"</string> <string name="notification_importance_high" msgid="3222680136612408223">"ამ შეტყობინებების სიის თავში, ხმოვან სიგნალთან ერთად ჩვენება"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ამ შეტყობინებების პირდაპირ ეკრანზე, ხმოვან სიგნალთან ერთად ჩვენება"</string> + <string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string> + <string name="notification_done" msgid="5279426047273930175">"მზადაა"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ჩვეულებრივი ფერები"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ღამის ფერები"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"მორგებული ფერები"</string> diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml index 5187b5410daf..eb8995e0ab7c 100644 --- a/packages/SystemUI/res/values-kk-rKZ/strings.xml +++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Осы хабарландыруларды үнсіз көрсету"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Хабарландыруларды тізімінің жоғарғы жағында көрсету және дыбыс шығару"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Экранға бекіту және дыбыс шығару"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string> + <string name="notification_done" msgid="5279426047273930175">"Дайын"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Қалыпты түстер"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Түнгі түстер"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Арнаулы түстер"</string> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index 5665a901992c..5c102131b9e7 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"បង្ហាញការជូនដំណឹងទាំងនេះស្ងាត់ៗ"</string> <string name="notification_importance_high" msgid="3222680136612408223">"បង្ហាញនៅផ្នែកខាងលើបញ្ជីនៃការជូនដំណឹង និងបន្លឺសំឡេង"</string> <string name="notification_importance_max" msgid="5236987171904756134">"លោតបង្ហាញនៅលើអេក្រង់ និងបន្លឺសំឡេង"</string> + <string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string> + <string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ពណ៌ធម្មតា"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ពណ៌ពេលយប់"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ពណ៌ផ្ទាល់ខ្លួន"</string> diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml index 0326a6726462..28b2de55f406 100644 --- a/packages/SystemUI/res/values-kn-rIN/strings.xml +++ b/packages/SystemUI/res/values-kn-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಸ್ಥಬ್ಧವಾಗಿ ತೋರಿಸು"</string> <string name="notification_importance_high" msgid="3222680136612408223">"ಅಧಿಸೂಚನೆಗಳ ಪಟ್ಟಿಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ ತೋರಿಸು ಮತ್ತು ಧ್ವನಿ ಮಾಡು"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಧ್ವನಿ ಮಾಡು"</string> + <string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> + <string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ಸಾಮಾನ್ಯ ಬಣ್ಣಗಳು"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ರಾತ್ರಿ ಬಣ್ಣಗಳು"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ಕಸ್ಟಮ್ ಬಣ್ಣಗಳು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index b910c0486e10..110376fb04f7 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"무음으로 알림 표시"</string> <string name="notification_importance_high" msgid="3222680136612408223">"알림 목록 상단에 표시하고 소리로 알림"</string> <string name="notification_importance_max" msgid="5236987171904756134">"화면에 표시하고 소리로 알림"</string> + <string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string> + <string name="notification_done" msgid="5279426047273930175">"완료"</string> <string name="color_matrix_none" msgid="2121957926040543148">"일반 색상"</string> <string name="color_matrix_night" msgid="5943817622105307072">"야간 색상"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"맞춤 색상"</string> diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml index e703e0093372..ee2ef5d78e27 100644 --- a/packages/SystemUI/res/values-ky-rKG/strings.xml +++ b/packages/SystemUI/res/values-ky-rKG/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Бул эскертмелер үнсүз көрсөтүлсүн"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Эскертмелер тизмесинин эң жогорусунда үн чыгарып көрсөтүлсүн"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Үн менен коштолуп, экранга чыгарылсын"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string> + <string name="notification_done" msgid="5279426047273930175">"Аткарылды"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Кадимки түстөр"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Түнкү түстөр"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Ыңгайлаштырылган түстөр"</string> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 8e4340818f0c..ed103ea4d089 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ສະແດງການແຈ້ງເຕືອນເຫຼົ່ານີ້ແບບບໍ່ມີສຽງ"</string> <string name="notification_importance_high" msgid="3222680136612408223">"ສະແດງຢູ່ສ່ວນເທິງຂອງລາຍການແຈ້ງເຕືອນ ແລະສົ່ງສຽງດັງ"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ເດັ້ງຂຶ້ນເທິງຫນ້າຈໍ ແລະສົ່ງສຽງດັງ"</string> + <string name="notification_more_settings" msgid="816306283396553571">"ການຕັ້ງຄ່າເພີ່ມເຕີມ"</string> + <string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ສີປົກກະຕິ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ສີຕອນກາງຄືນ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ສີແບບກຳນົດເອງ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 04066a71e7a3..ba98e2b1df9e 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tyliai rodyti šiuos pranešimus"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Rodyti pranešimų sąrašo viršuje ir skambėti"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Rodyti ekrane ir skambėti"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string> + <string name="notification_done" msgid="5279426047273930175">"Atlikta"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Įprastos spalvos"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nakties spalvos"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Tinkintos spalvos"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 649d07b93515..0143fea7c7bb 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -461,6 +461,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Rādīt šos paziņojumus bez skaņas signāla"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Rādīt paziņojumu saraksta augšdaļā un ar skaņas signālu"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Rādīt ekrānā ar skaņas signālu"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Citi iestatījumi"</string> + <string name="notification_done" msgid="5279426047273930175">"Gatavs"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Parastas krāsas"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nakts krāsas"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Pielāgotas krāsas"</string> diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml index 928bbcd556cb..c57313936b6f 100644 --- a/packages/SystemUI/res/values-mk-rMK/strings.xml +++ b/packages/SystemUI/res/values-mk-rMK/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Тивко прикажувај ги известувањава"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Прикажувај ги на врвот на списокот со известувања и дај звучен сигнал"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Појави се на екранот и дај звучен сигнал"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string> + <string name="notification_done" msgid="5279426047273930175">"Готово"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Нормални бои"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Ноќни бои"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Приспособени бои"</string> diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml index 2f4b81abd1db..c6a08e600e17 100644 --- a/packages/SystemUI/res/values-ml-rIN/strings.xml +++ b/packages/SystemUI/res/values-ml-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ഈ അറിയിപ്പുകൾ നിശബ്ദമായി കാണിക്കുക"</string> <string name="notification_importance_high" msgid="3222680136612408223">"അറിയിപ്പ് ലിസ്റ്റിന്റെ ഏറ്റവും മുകളിൽ കാണിക്കുക, ശബ്ദമുണ്ടാക്കുക"</string> <string name="notification_importance_max" msgid="5236987171904756134">"സ്ക്രീനിൽ ദൃശ്യമാക്കുക, ശബ്ദമുണ്ടാക്കുക"</string> + <string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string> + <string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string> <string name="color_matrix_none" msgid="2121957926040543148">"സാധാരണ വര്ണ്ണങ്ങൾ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"രാത്രി വര്ണ്ണങ്ങൾ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ഇഷ്ടാനുസൃത വര്ണ്ണങ്ങൾ"</string> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 3dd084ea64db..91c817a5caf4 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -458,6 +458,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Эдгээр мэдэгдлийг дуугүй харуулах"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Мэдэгдлийг жагсаалтын эхэнд дуутай харуулах"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Дэлгэцэнд яаралтайгаар дуутай гаргах"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Бусад тохиргоо"</string> + <string name="notification_done" msgid="5279426047273930175">"Дууссан"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Хэвийн өнгө"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Шөнийн өнгө"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Өгөгдмөл өнгө"</string> diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml index 59eadf1cadf0..5d3a96f7721e 100644 --- a/packages/SystemUI/res/values-mr-rIN/strings.xml +++ b/packages/SystemUI/res/values-mr-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"या सूचना शांतपणे दर्शवा"</string> <string name="notification_importance_high" msgid="3222680136612408223">"सूचना सूचीच्या शीर्षस्थानी दर्शवा आणि ध्वनी चालू करा"</string> <string name="notification_importance_max" msgid="5236987171904756134">"स्क्रीनवर डोकावून पहा आणि ध्वनी चालू करा"</string> + <string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string> + <string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string> <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रंग"</string> <string name="color_matrix_night" msgid="5943817622105307072">"रात्रीचे रंग"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"सानुकूल रंग"</string> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 5dd221ac22ca..f0a013c4f93a 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tunjukkan pemberitahuan ini secara senyap"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Tunjukkan pada bahagian atas senarai pemberitahuan dan bunyikan"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Intai pada skrin dan bunyikan"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string> + <string name="notification_done" msgid="5279426047273930175">"Selesai"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Warna biasa"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Warna malam"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Warna tersuai"</string> diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml index 293e01047114..75008cbd5004 100644 --- a/packages/SystemUI/res/values-my-rMM/strings.xml +++ b/packages/SystemUI/res/values-my-rMM/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ဤသတိပေးချက်များကို တိတ်တဆိတ်ပြပါ"</string> <string name="notification_importance_high" msgid="3222680136612408223">"သတိပေးချက်စာရင်းများ၏ ထိပ်တွင်ပြကာ အသံဖွင့်ပါ"</string> <string name="notification_importance_max" msgid="5236987171904756134">"မျက်နှာပြင်ပေါ်သို့ ဖော်ပြကာ အသံဖွင့်ပါ"</string> + <string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string> + <string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ပုံမှန် အရောင်များ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ည အရောင်များ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"စိတ်ကြိုက် အရောင်များ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index ffc2fcf2eff7..295a49a8f555 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Vis disse varslene uten lyd"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Vis øverst på varsellisten med lyd"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Vis fort på skjermen med lyd"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string> + <string name="notification_done" msgid="5279426047273930175">"Ferdig"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normale farger"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nattfarger"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Spesialtilpassede farger"</string> diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml index 6f40d554f390..d0aa5103532a 100644 --- a/packages/SystemUI/res/values-ne-rNP/strings.xml +++ b/packages/SystemUI/res/values-ne-rNP/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"यी सूचनाहरू बिना आवाज देखाउनुहोस्"</string> <string name="notification_importance_high" msgid="3222680136612408223">"सूचना सूचीको शीर्षमा देखाउनुहोस् र आवाज निकाल्नुहोस्"</string> <string name="notification_importance_max" msgid="5236987171904756134">"स्क्रिनमा हेर्नुहोस् र आवाज निकाल्नुहोस्"</string> + <string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string> + <string name="notification_done" msgid="5279426047273930175">"सम्पन्न भयो"</string> <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रङहरू"</string> <string name="color_matrix_night" msgid="5943817622105307072">"रात्री रङहरू"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"अनुकूलन रङहरू"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 27eb1d11a427..75812ad3e2ca 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Deze meldingen zonder geluid weergeven"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Boven aan de lijst met meldingen weergeven en geluid laten horen"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Op het scherm weergeven en geluid laten horen"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Meer instellingen"</string> + <string name="notification_done" msgid="5279426047273930175">"Gereed"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normale kleuren"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nachtkleuren"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Aangepaste kleuren"</string> diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml index 2dd91eca3e9b..fc5a3eaa44ed 100644 --- a/packages/SystemUI/res/values-pa-rIN/strings.xml +++ b/packages/SystemUI/res/values-pa-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚੁੱਪਚਾਪ ਢੰਗ ਨਾਲ ਵਿਖਾਓ"</string> <string name="notification_importance_high" msgid="3222680136612408223">"ਸੂਚਨਾਵਾਂ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਵਿਖਾਓ ਅਤੇ ਆਵਾਜ਼ ਕਰੋ"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ਸਕਰੀਨ \'ਤੇ ਝਾਤੀ ਮਾਰੋ ਅਤੇ ਆਵਾਜ਼ ਕਰੋ"</string> + <string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string> + <string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string> <string name="color_matrix_none" msgid="2121957926040543148">"ਸਧਾਰਨ ਰੰਗ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"ਰਾਤ ਦੇ ਰੰਗ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"ਕਸਟਮ ਰੰਗ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index bf222bcd6bdb..8ef50c993d5b 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Pokazuj te powiadomienia bez sygnału dźwiękowego"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Pokazuj na górze listy powiadomień i sygnalizuj dźwiękiem"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Wyświetlaj na ekranie i odtwarzaj dźwięk"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string> + <string name="notification_done" msgid="5279426047273930175">"Gotowe"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Kolory standardowe"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Kolory nocne"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Kolory niestandardowe"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 5d43efa8358e..8f614781c84f 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar essas notificações de forma silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar parcialmente na tela e emitir som"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string> + <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 44ecd41fc763..8a727a1e4024 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -425,7 +425,7 @@ <string name="status_bar_airplane" msgid="7057575501472249002">"Modo de avião"</string> <string name="add_tile" msgid="2995389510240786221">"Adicionar mosaico"</string> <string name="broadcast_tile" msgid="3894036511763289383">"Mosaico de transmissão"</string> - <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Só vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g> se desativar esta funcionalidade antes dessa hora"</string> + <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Só vai ouvir o próximo alarme (<xliff:g id="WHEN">%1$s</xliff:g>) se desativar esta funcionalidade antes dessa hora"</string> <string name="zen_alarm_warning" msgid="444533119582244293">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template" msgid="3980063409350522735">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="4242179982586714810">"em <xliff:g id="WHEN">%1$s</xliff:g>"</string> @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificações sem som"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar no ecrã e emitir som"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string> + <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 5d43efa8358e..8f614781c84f 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar essas notificações de forma silenciosa"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar parcialmente na tela e emitir som"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string> + <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index e9e7bdc0d4c5..3ab8bf2a534f 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -461,6 +461,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Aceste notificări se afișează fără a se emite un sunet"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Se afișează în partea de sus a listei cu notificări și se emite un sunet"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Se afișează pentru o scurtă durată pe ecran și se emite un sunet"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string> + <string name="notification_done" msgid="5279426047273930175">"Terminat"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Culori normale"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Culori de noapte"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Culori personalizate"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index b68b434be016..6ffd3330822e 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Показывать уведомления без звука."</string> <string name="notification_importance_high" msgid="3222680136612408223">"Показывать со звуком в начале списка уведомлений."</string> <string name="notification_importance_max" msgid="5236987171904756134">"Показывать со звуком поверх всех окон."</string> + <string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string> + <string name="notification_done" msgid="5279426047273930175">"Готово"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Обычные цвета"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Ночные цвета"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Собственные цвета"</string> diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml index b9f7e5364c90..efbc0423e1dc 100644 --- a/packages/SystemUI/res/values-si-rLK/strings.xml +++ b/packages/SystemUI/res/values-si-rLK/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"නිශ්ශබ්දව මෙම දැනුම්දීම් පෙන්වන්න"</string> <string name="notification_importance_high" msgid="3222680136612408223">"දැනුම්දීම් ලැයිස්තුවෙහි ඉහළින්ම පෙන්වන්න සහ ශබ්ද කරන්න"</string> <string name="notification_importance_max" msgid="5236987171904756134">"තිරයට පැමිණ ශබ්ද කරන්න"</string> + <string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string> + <string name="notification_done" msgid="5279426047273930175">"නිමයි"</string> <string name="color_matrix_none" msgid="2121957926040543148">"සාමාන්ය වර්ණ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"රාත්රී වර්ණ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"අභිරුචි වර්ණ"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 91a143e58f47..31985b4e9624 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tieto upozornenia zobrazovať bez zvukového signálu"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Zobrazovať v hornej časti zoznamu upozornení so zvukovým signálom"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Zobrazovať cez obrazovku so zvukovým signálom"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string> + <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normálne farby"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nočné farby"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Vlastné farby"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 96974ae96dd0..7c8d443ba66b 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Prikaži ta obvestila brez zvoka"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Prikaži na vrhu seznama obvestil in predvajaj zvok"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Za hip pokaži predogled na zaslonu in predvajaj zvok"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string> + <string name="notification_done" msgid="5279426047273930175">"Dokončano"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Običajne barve"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nočne barve"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Barve po meri"</string> diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml index b904ae197736..c8add9497b31 100644 --- a/packages/SystemUI/res/values-sq-rAL/strings.xml +++ b/packages/SystemUI/res/values-sq-rAL/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Shfaqi këto njoftime në heshtje"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Shfaqi në krye të listës së njoftimeve dhe lësho tingull"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Shfaq një vështrim të shpejtë në ekran dhe lësho tingull"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string> + <string name="notification_done" msgid="5279426047273930175">"U krye"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Ngjyrat normale"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Ngjyrat e natës"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Ngjyrat e personalizuara"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 6470fcc07265..bec1fd4d4217 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -455,12 +455,14 @@ <string name="low_importance" msgid="4109929986107147930">"Мала важност"</string> <string name="default_importance" msgid="8192107689995742653">"Уобичајена важност"</string> <string name="high_importance" msgid="1527066195614050263">"Велика важност"</string> - <string name="max_importance" msgid="5089005872719563894">"Највећа важност"</string> + <string name="max_importance" msgid="5089005872719563894">"Важност: хитно"</string> <string name="notification_importance_blocked" msgid="2397192642657872872">"Ова обавештења се никада не приказују"</string> <string name="notification_importance_low" msgid="4383563267370859725">"Приказују се у дну листе обавештења без звука"</string> <string name="notification_importance_default" msgid="4926529615920610817">"Ова обавештења се приказују без звука"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Приказују се у врху листе обавештења и емитује се звук"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Накратко се приказују на екрану и емитује се звук"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string> + <string name="notification_done" msgid="5279426047273930175">"Готово"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Нормалне боје"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Ноћне боје"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Прилагођене боје"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 5f2b397c772d..d4a9cdc31b3e 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Visa aviseringarna utan ljud"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Visa högst upp på listan, med ljud"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Visa på skärmen, med ljud"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string> + <string name="notification_done" msgid="5279426047273930175">"Klar"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normala färger"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Nattfärger"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Anpassade färger"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 8f962dab4f7b..2ffbf2ba3c19 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Onyesha arifa hizi bila sauti"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Onyesha katika sehemu ya juu ya orodha ya arifa na itoe sauti"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Weka onyesho la kuchungulia kwenye skrini na itoe sauti"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string> + <string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Rangi za kawaida"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Rangi za usiku"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Rangi maalum"</string> diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml index a625c6fe8239..168a11b4f07a 100644 --- a/packages/SystemUI/res/values-ta-rIN/strings.xml +++ b/packages/SystemUI/res/values-ta-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ஒலியின்றி இந்த அறிவிப்புகளைக் காட்டு"</string> <string name="notification_importance_high" msgid="3222680136612408223">"அறிவிப்புகள் பட்டியலின் மேல் பகுதியில் ஒலியுடன் காட்டு"</string> <string name="notification_importance_max" msgid="5236987171904756134">"ஒலியுடன் திரையில் காட்டு"</string> + <string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string> + <string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string> <string name="color_matrix_none" msgid="2121957926040543148">"இயல்பான வண்ணங்கள்"</string> <string name="color_matrix_night" msgid="5943817622105307072">"இரவுநேர வண்ணங்கள்"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"தனிப்பயன் வண்ணங்கள்"</string> diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml index 9d1403f57473..086dede50a14 100644 --- a/packages/SystemUI/res/values-te-rIN/strings.xml +++ b/packages/SystemUI/res/values-te-rIN/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"ఈ నోటిఫికేషన్లను శబ్దం లేకుండా చూపుతుంది"</string> <string name="notification_importance_high" msgid="3222680136612408223">"నోటిఫికేషన్ల జాబితా ఎగువ భాగంలో శబ్దంతో చూపుతుంది"</string> <string name="notification_importance_max" msgid="5236987171904756134">"స్క్రీన్పై శీఘ్రంగా శబ్దంతో చూపుతుంది"</string> + <string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్లు"</string> + <string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string> <string name="color_matrix_none" msgid="2121957926040543148">"సాధారణ రంగులు"</string> <string name="color_matrix_night" msgid="5943817622105307072">"రాత్రి రంగులు"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"అనుకూల రంగులు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 38e028c55c1d..8c1a7d35f680 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"แสดงการแจ้งเตือนเหล่านี้โดยไม่ส่งเสียง"</string> <string name="notification_importance_high" msgid="3222680136612408223">"แสดงที่ด้านบนของรายการแจ้งเตือนและส่งเสียง"</string> <string name="notification_importance_max" msgid="5236987171904756134">"แสดงบนหน้าจอในช่วงเวลาสั้นๆ และส่งเสียง"</string> + <string name="notification_more_settings" msgid="816306283396553571">"การตั้งค่าเพิ่มเติม"</string> + <string name="notification_done" msgid="5279426047273930175">"เสร็จสิ้น"</string> <string name="color_matrix_none" msgid="2121957926040543148">"สีปกติ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"สียามค่ำคืน"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"สีที่กำหนดเอง"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 5815041cd7c2..4bee3eab3b26 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Tahimik na ipakita ang mga notification na ito"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Ipakita sa itaas ng listahan ng mga notification at mag-play ng tunog"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Ipasilip sa screen at mag-play ng tunog"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Higit pang mga setting"</string> + <string name="notification_done" msgid="5279426047273930175">"Tapos Na"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Mga karaniwang kulay"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Madidilim na kulay"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Mga custom na kulay"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 6832808d7ffe..c0ecc780f36e 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildirimleri sessizce göster"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Bildirim listesinin en üstünde göster ve ses çıkar"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Ekrana getir ve ses çıkar"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string> + <string name="notification_done" msgid="5279426047273930175">"Bitti"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Normal renkler"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Gece renkleri"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Özel renkler"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 973602801af6..31094033aa66 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -462,6 +462,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Показувати ці сповіщення без звукового сигналу"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Показувати сповіщення вгорі списку зі звуковим сигналом"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Показувати сповіщення на екрані зі звуковим сигналом"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string> + <string name="notification_done" msgid="5279426047273930175">"Готово"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Стандартні кольори"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Нічні кольори"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Користувацькі кольори"</string> diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml index 28484eb0efd3..52aa58032c8a 100644 --- a/packages/SystemUI/res/values-ur-rPK/strings.xml +++ b/packages/SystemUI/res/values-ur-rPK/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"خاموشی سے یہ اطلاعات دکھائیں"</string> <string name="notification_importance_high" msgid="3222680136612408223">"اطلاعات کی فہرست پر سب سے اوپر دکھائیں اور آواز چلائیں"</string> <string name="notification_importance_max" msgid="5236987171904756134">"اسکرین پر دکھائیں اور آواز چلائیں"</string> + <string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string> + <string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string> <string name="color_matrix_none" msgid="2121957926040543148">"عام رنگ"</string> <string name="color_matrix_night" msgid="5943817622105307072">"رات کے رنگ"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"حسب ضرورت رنگ"</string> diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index 4bd9dbcdea2a..a849cfc6de21 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildirishnomalar ovozsiz ko‘rsatilsin"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Bildirishnomalar ro‘yxatining boshida ovoz bilan ko‘rsatilsin"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Barcha oynalar ustida signal ovozi bilan ko‘rsatilsin"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string> + <string name="notification_done" msgid="5279426047273930175">"Tayyor"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Odatiy ranglar"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Qoramtir ranglar"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Foydalanuvchi rangi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index c094d5b875d8..fa835ba0153e 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Hiển thị im lặng các thông báo này"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Hiển thị ở đầu danh sách thông báo và phát ra âm thanh"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Hiển thị trên màn hình và phát ra âm thanh"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string> + <string name="notification_done" msgid="5279426047273930175">"Xong"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Màu thông thường"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Màu tối"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Màu tùy chỉnh"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 3af77c201031..bb8f4adf70e1 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -460,6 +460,10 @@ <string name="notification_importance_default" msgid="4926529615920610817">"显示这些通知,但不发出提示音"</string> <string name="notification_importance_high" msgid="3222680136612408223">"在通知列表顶部显示,并发出提示音"</string> <string name="notification_importance_max" msgid="5236987171904756134">"在屏幕上持续显示,并发出提示音"</string> + <!-- no translation found for notification_more_settings (816306283396553571) --> + <skip /> + <!-- no translation found for notification_done (5279426047273930175) --> + <skip /> <string name="color_matrix_none" msgid="2121957926040543148">"常规颜色"</string> <string name="color_matrix_night" msgid="5943817622105307072">"夜间颜色"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"自定义颜色"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 8591785632a2..0782354a619d 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -454,12 +454,14 @@ <string name="low_importance" msgid="4109929986107147930">"低重要性"</string> <string name="default_importance" msgid="8192107689995742653">"一般重要性"</string> <string name="high_importance" msgid="1527066195614050263">"高重要性"</string> - <string name="max_importance" msgid="5089005872719563894">"緊急"</string> + <string name="max_importance" msgid="5089005872719563894">"緊急重要性"</string> <string name="notification_importance_blocked" msgid="2397192642657872872">"永不顯示這些通知"</string> <string name="notification_importance_low" msgid="4383563267370859725">"顯示在通知清單底部但不發出音效"</string> <string name="notification_importance_default" msgid="4926529615920610817">"顯示這些通知但不發出音效"</string> <string name="notification_importance_high" msgid="3222680136612408223">"顯示在通知清單頂部並發出音效"</string> <string name="notification_importance_max" msgid="5236987171904756134">"不時於螢幕出現並發出音效"</string> + <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string> + <string name="notification_done" msgid="5279426047273930175">"完成"</string> <string name="color_matrix_none" msgid="2121957926040543148">"一般色系"</string> <string name="color_matrix_night" msgid="5943817622105307072">"深沉色系"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"自訂顏色"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 34fa7aa5f3bc..29520f6f38fc 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"顯示這些通知且不發出任何音效"</string> <string name="notification_importance_high" msgid="3222680136612408223">"顯示在通知清單頂端並發出音效"</string> <string name="notification_importance_max" msgid="5236987171904756134">"短暫顯示在螢幕上並發出音效"</string> + <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string> + <string name="notification_done" msgid="5279426047273930175">"完成"</string> <string name="color_matrix_none" msgid="2121957926040543148">"一般顏色"</string> <string name="color_matrix_night" msgid="5943817622105307072">"夜間顏色"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"自訂顏色"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index ebd857cc9a8e..3ee46eaa1ef2 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -460,6 +460,8 @@ <string name="notification_importance_default" msgid="4926529615920610817">"Bonisa ngokuthulile lezi zaziso"</string> <string name="notification_importance_high" msgid="3222680136612408223">"Bonisa ngaphezulu kohlu lwezaziso uphinde wenze umsindo"</string> <string name="notification_importance_max" msgid="5236987171904756134">"Bheka kusikrini uphinde wenze umsindo"</string> + <string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string> + <string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string> <string name="color_matrix_none" msgid="2121957926040543148">"Imibala ejwayelekile"</string> <string name="color_matrix_night" msgid="5943817622105307072">"Imibala yasebusuku"</string> <string name="color_matrix_custom" msgid="3655576492322298713">"Imibala yangokwezifiso"</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 45ddd509e96d..876c21efe1a8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1228,6 +1228,11 @@ <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description --> <string name="notification_importance_max">Peek onto the screen and make sound</string> + <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] --> + <string name="notification_more_settings">More settings</string> + <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> + <string name="notification_done">Done</string> + <!-- Label for no color transform [CHAR LIMIT=30] --> <string name="color_matrix_none">Normal colors</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 3b0ab791f03c..527b6380d2f3 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -317,4 +317,12 @@ <item name="@dropdownPreferenceStyle">@style/Preference.DropDown.Material</item> </style> + <style name="TextAppearance.NotificationGuts"> + <item name="android:textSize">14sp</item> + <item name="android:textColor">@color/notification_guts_btn_color</item> + <item name="android:textAllCaps">true</item> + <item name="android:fontFamily">sans-serif-medium</item> + <item name="android:gravity">center</item> + </style> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java index b56ad76ec2b4..01eb5f941fee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java @@ -75,7 +75,8 @@ public class QSIconView extends ViewGroup { iv.setTag(R.id.qs_icon_tag, state.icon); if (d instanceof Animatable) { Animatable a = (Animatable) d; - if (state.icon instanceof QSTile.AnimationIcon && !iv.isShown()) { + a.start(); + if (!iv.isShown()) { a.stop(); // skip directly to end state } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 39f0c5578e8e..1a36abd3f1c3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -19,8 +19,6 @@ package com.android.systemui.qs; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; @@ -29,9 +27,20 @@ import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; - import com.android.systemui.qs.QSTile.State; -import com.android.systemui.statusbar.policy.*; +import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.policy.HotspotController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.Listenable; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.ZenModeController; import java.util.Collection; import java.util.Objects; @@ -389,11 +398,7 @@ public abstract class QSTile<TState extends State> implements Listenable { @Override public Drawable getDrawable(Context context) { - Drawable d = context.getDrawable(mResId); - if (d instanceof Animatable) { - ((Animatable) d).start(); - } - return d; + return context.getDrawable(mResId); } @Override @@ -408,41 +413,14 @@ public abstract class QSTile<TState extends State> implements Listenable { } protected class AnimationIcon extends ResourceIcon { - private boolean mAllowAnimation; - public AnimationIcon(int resId) { super(resId); } - public void setAllowAnimation(boolean allowAnimation) { - mAllowAnimation = allowAnimation; - } - @Override public Drawable getDrawable(Context context) { // workaround: get a clean state for every new AVD - final AnimatedVectorDrawable d = (AnimatedVectorDrawable) context.getDrawable(mResId) - .getConstantState().newDrawable(); - d.start(); - if (mAllowAnimation) { - mAllowAnimation = false; - } else { - d.stop(); // skip directly to end state - } - return d; - } - } - - protected enum UserBoolean { - USER_TRUE(true, true), - USER_FALSE(true, false), - BACKGROUND_TRUE(false, true), - BACKGROUND_FALSE(false, false); - public final boolean value; - public final boolean userInitiated; - private UserBoolean(boolean userInitiated, boolean value) { - this.value = value; - this.userInitiated = userInitiated; + return context.getDrawable(mResId).getConstantState().newDrawable(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java deleted file mode 100644 index a5e1fd55c8f0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.android.systemui.qs; - -import android.os.IBinder; -import android.service.quicksettings.IQSTileService; -import android.service.quicksettings.Tile; -import android.util.Log; - - -public class QSTileServiceWrapper implements IQSTileService { - private static final String TAG = "IQSTileServiceWrapper"; - - private final IQSTileService mService; - - public QSTileServiceWrapper(IQSTileService service) { - mService = service; - } - - @Override - public IBinder asBinder() { - return mService.asBinder(); - } - - @Override - public void setQSTile(Tile tile) { - try { - mService.setQSTile(tile); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } - - @Override - public void onTileAdded() { - try { - mService.onTileAdded(); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } - - @Override - public void onTileRemoved() { - try { - mService.onTileRemoved(); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } - - @Override - public void onStartListening() { - try { - mService.onStartListening(); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } - - @Override - public void onStopListening() { - try { - mService.onStopListening(); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } - - @Override - public void onClick(IBinder token) { - try { - mService.onClick(token); - } catch (Exception e) { - Log.d(TAG, "Caught exception from QSTileService", e); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java index 87c2973592b6..cdba3eb56f1f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java @@ -35,8 +35,8 @@ import android.view.View; import com.android.systemui.R; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTile; -import com.android.systemui.qs.QSTileServiceWrapper; -import com.android.systemui.qs.tiles.CustomTile; +import com.android.systemui.qs.external.QSTileServiceWrapper; +import com.android.systemui.qs.external.CustomTile; import com.android.systemui.statusbar.phone.QSTileHost; import com.android.systemui.tuner.TunerService; @@ -179,14 +179,16 @@ public class CustomQSPanel extends QSPanel { mCurrentTiles.clear(); for (int i = 0; i < mTiles.size(); i++) { if (mTiles.get(i).startsWith(CustomTile.PREFIX)) { - mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i))); + QSTile<?> tile = BlankCustomTile.create(mHost, mTiles.get(i)); + tile.setTileSpec(mTiles.get(i)); + mCurrentTiles.add(tile); } else { QSTile<?> tile = mHost.createTile(mTiles.get(i)); if (tile != null) { + tile.setTileSpec(mTiles.get(i)); mCurrentTiles.add(tile); } } - mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i)); } super.setTiles(mCurrentTiles); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 6706c7aace12..a6a71439dbae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -41,7 +41,7 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTile.Icon; -import com.android.systemui.qs.tiles.CustomTile; +import com.android.systemui.qs.external.CustomTile; import com.android.systemui.statusbar.phone.QSTileHost; import com.android.systemui.tuner.QSPagingSwitch; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index bb74f341b89d..2e5a0b277552 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -11,32 +11,26 @@ * 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. + * limitations under the License */ +package com.android.systemui.qs.external; -package com.android.systemui.qs.tiles; - -import android.app.ActivityManager; -import android.app.Service; import android.content.ComponentName; -import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.service.quicksettings.IQSTileService; import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManager; import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.systemui.qs.QSTile; -import com.android.systemui.qs.QSTileServiceWrapper; import com.android.systemui.statusbar.phone.QSTileHost; public class CustomTile extends QSTile<QSTile.State> { @@ -52,8 +46,9 @@ public class CustomTile extends QSTile<QSTile.State> { private final Tile mTile; private final IWindowManager mWindowManager; private final IBinder mToken = new Binder(); + private final IQSTileService mService; + private final TileServiceManager mServiceManager; - private QSTileServiceWrapper mService; private boolean mListening; private boolean mBound; private boolean mIsTokenGranted; @@ -63,7 +58,9 @@ public class CustomTile extends QSTile<QSTile.State> { super(host); mWindowManager = WindowManagerGlobal.getWindowManagerService(); mComponent = ComponentName.unflattenFromString(action); - mTile = new Tile(mComponent, host); + mServiceManager = host.getTileServices().getTileWrapper(this); + mService = mServiceManager.getTileService(); + mTile = new Tile(mComponent); try { PackageManager pm = mContext.getPackageManager(); ServiceInfo info = pm.getServiceInfo(mComponent, 0); @@ -72,6 +69,11 @@ public class CustomTile extends QSTile<QSTile.State> { mTile.setLabel(info.loadLabel(pm)); } catch (Exception e) { } + try { + mService.setQSTile(mTile); + } catch (RemoteException e) { + // Called through wrapper, won't happen here. + } } public ComponentName getComponent() { @@ -96,42 +98,33 @@ public class CustomTile extends QSTile<QSTile.State> { public void setListening(boolean listening) { if (mListening == listening) return; mListening = listening; - if (listening) { - mHandler.removeCallbacks(mUnbind); - if (!mBound) { - // TODO: Guarantee re-bind on user-switch. - mContext.bindServiceAsUser(new Intent().setComponent(mComponent), - mServiceConnection, Service.BIND_AUTO_CREATE, - new UserHandle(ActivityManager.getCurrentUser())); - mBound = true; - } else { - if (mService != null) { + try { + if (listening) { + if (mServiceManager.getType() == TileService.TILE_MODE_PASSIVE) { + mServiceManager.setBindRequested(true); mService.onStartListening(); - } else { - Log.d(TAG, "Can't start service listening"); } - } - } else { - if (mService != null) { + } else { mService.onStopListening(); - } - if (mIsTokenGranted && !mIsShowingDialog) { - try { - if (DEBUG) Log.d(TAG, "Removing token"); - mWindowManager.removeWindowToken(mToken); - } catch (RemoteException e) { + if (mIsTokenGranted && !mIsShowingDialog) { + try { + if (DEBUG) Log.d(TAG, "Removing token"); + mWindowManager.removeWindowToken(mToken); + } catch (RemoteException e) { + } + mIsTokenGranted = false; } - mIsTokenGranted = false; + mIsShowingDialog = false; + mServiceManager.setBindRequested(false); } - mIsShowingDialog = false; - mHandler.postDelayed(mUnbind, UNBIND_DELAY); + } catch (RemoteException e) { + // Called through wrapper, won't happen here. } } @Override protected void handleDestroy() { super.handleDestroy(); - mHandler.removeCallbacks(mUnbind); if (mIsTokenGranted) { try { if (DEBUG) Log.d(TAG, "Removing token"); @@ -139,7 +132,6 @@ public class CustomTile extends QSTile<QSTile.State> { } catch (RemoteException e) { } } - mUnbind.run(); } @Override @@ -154,16 +146,20 @@ public class CustomTile extends QSTile<QSTile.State> { @Override protected void handleClick() { - if (mService != null) { - try { - if (DEBUG) Log.d(TAG, "Adding token"); - mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG); - mIsTokenGranted = true; - } catch (RemoteException e) { + try { + if (DEBUG) Log.d(TAG, "Adding token"); + mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG); + mIsTokenGranted = true; + } catch (RemoteException e) { + } + try { + if (mServiceManager.getType() == TileService.TILE_MODE_ACTIVE) { + mServiceManager.setBindRequested(true); + mService.onStartListening(); } mService.onClick(mToken); - } else { - Log.e(TAG, "Click with no service " + getTileSpec()); + } catch (RemoteException e) { + // Called through wrapper, won't happen here. } MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName()); } @@ -187,34 +183,9 @@ public class CustomTile extends QSTile<QSTile.State> { @Override public int getMetricsCategory() { - return MetricsLogger.QS_INTENT; + return MetricsLogger.QS_CUSTOM; } - private final ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service)); - if (mListening) { - mService.setQSTile(mTile); - mService.onStartListening(); - } else { - mService.onStopListening(); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - } - }; - - private final Runnable mUnbind = new Runnable() { - @Override - public void run() { - mContext.unbindService(mServiceConnection); - mBound = false; - } - }; - public static ComponentName getComponentFromSpec(String spec) { final String action = spec.substring(PREFIX.length(), spec.length() - 1); if (action.isEmpty()) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java new file mode 100644 index 000000000000..d41cdde37958 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.os.IBinder; +import android.service.quicksettings.IQSService; +import android.service.quicksettings.IQSTileService; +import android.service.quicksettings.Tile; +import android.util.Log; + + +public class QSTileServiceWrapper { + private static final String TAG = "IQSTileServiceWrapper"; + + private final IQSTileService mService; + + public QSTileServiceWrapper(IQSTileService service) { + mService = service; + } + + public IBinder asBinder() { + return mService.asBinder(); + } + + public boolean setQSTile(Tile tile) { + try { + mService.setQSTile(tile); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean onTileAdded() { + try { + mService.onTileAdded(); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean onTileRemoved() { + try { + mService.onTileRemoved(); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean onStartListening() { + try { + mService.onStartListening(); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean onStopListening() { + try { + mService.onStopListening(); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean onClick(IBinder token) { + try { + mService.onClick(token); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } + + public boolean setQSService(IQSService service) { + try { + mService.setQSService(service); + return true; + } catch (Exception e) { + Log.d(TAG, "Caught exception from TileService", e); + return false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java new file mode 100644 index 000000000000..41fce9f1fb98 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.app.AppGlobals; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.net.Uri; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.quicksettings.IQSService; +import android.service.quicksettings.IQSTileService; +import android.service.quicksettings.Tile; +import android.support.annotation.VisibleForTesting; +import android.util.ArraySet; +import android.util.Log; +import libcore.util.Objects; + +import java.util.Set; + +/** + * Manages the lifecycle of a TileService. + * <p> + * Will keep track of all calls on the IQSTileService interface and will relay those calls to the + * TileService as soon as it is bound. It will only bind to the service when it is allowed to + * ({@link #setBindService(boolean)}) and when the service is available. + */ +public class TileLifecycleManager extends BroadcastReceiver implements + IQSTileService, ServiceConnection, IBinder.DeathRecipient { + public static final boolean DEBUG = false; + + private static final String TAG = "TileLifecycleManager"; + + private static final int MSG_ON_ADDED = 0; + private static final int MSG_ON_REMOVED = 1; + private static final int MSG_ON_CLICK = 2; + + // Bind retry control. + private static final int MAX_BIND_RETRIES = 5; + private static final int BIND_RETRY_DELAY = 1000; + + private final Context mContext; + private final Handler mHandler; + private final Intent mIntent; + private final UserHandle mUser; + + private Set<Integer> mQueuedMessages = new ArraySet<>(); + private QSTileServiceWrapper mWrapper; + private boolean mListening; + private Tile mTile; + private IBinder mClickBinder; + + private int mBindTryCount; + private boolean mBound; + @VisibleForTesting + boolean mReceiverRegistered; + private IQSService mService; + + public TileLifecycleManager(Handler handler, Context context, Intent intent, UserHandle user) { + mContext = context; + mHandler = handler; + mIntent = intent; + mUser = user; + } + + public ComponentName getComponent() { + return mIntent.getComponent(); + } + + public boolean hasPendingClick() { + synchronized (mQueuedMessages) { + return mQueuedMessages.contains(MSG_ON_CLICK); + } + } + + public void setBindService(boolean bind) { + mBound = bind; + if (bind) { + if (mBindTryCount == MAX_BIND_RETRIES) { + // Too many failures, give up on this tile until an update. + startPackageListening(); + return; + } + if (!checkComponentState()) { + return; + } + if (DEBUG) Log.d(TAG, "Binding service " + mIntent); + mBindTryCount++; + mContext.bindServiceAsUser(mIntent, this, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, + mUser); + } else { + if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent); + // Give it another chance next time it needs to be bound, out of kindness. + mBindTryCount = 0; + mWrapper = null; + mContext.unbindService(this); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.d(TAG, "onServiceConnected " + name); + // Got a connection, set the binding count to 0. + mBindTryCount = 0; + mWrapper = new QSTileServiceWrapper(Stub.asInterface(service)); + try { + service.linkToDeath(this, 0); + } catch (RemoteException e) { + } + setQSService(mService); + setQSTile(mTile); + handlePendingMessages(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name); + mWrapper = null; + } + + private void handlePendingMessages() { + // This ordering is laid out manually to make sure we preserve the TileService + // lifecycle. + ArraySet<Integer> queue; + synchronized (mQueuedMessages) { + queue = new ArraySet<>(mQueuedMessages); + mQueuedMessages.clear(); + } + if (queue.contains(MSG_ON_ADDED)) { + if (DEBUG) Log.d(TAG, "Handling pending onAdded"); + onTileAdded(); + } + if (mListening) { + if (DEBUG) Log.d(TAG, "Handling pending onStartListening"); + onStartListening(); + } + if (queue.contains(MSG_ON_CLICK)) { + if (DEBUG) Log.d(TAG, "Handling pending onClick"); + if (!mListening) { + Log.w(TAG, "Managed to get click on non-listening state..."); + // Skipping click since lost click privileges. + } else { + onClick(mClickBinder); + } + } + if (queue.contains(MSG_ON_REMOVED)) { + if (DEBUG) Log.d(TAG, "Handling pending onRemoved"); + if (mListening) { + Log.w(TAG, "Managed to get remove in listening state..."); + onStopListening(); + } + onTileRemoved(); + } + } + + public void handleDestroy() { + if (DEBUG) Log.d(TAG, "handleDestroy"); + if (mReceiverRegistered) { + stopPackageListening(); + } + } + + private void handleDeath() { + if (mWrapper == null) return; + mWrapper = null; + if (!mBound) return; + if (DEBUG) Log.d(TAG, "handleDeath"); + if (checkComponentState()) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (mBound) { + // Retry binding. + setBindService(true); + } + } + }, BIND_RETRY_DELAY); + } + } + + @Override + public void setQSTile(Tile tile) { + if (DEBUG) Log.d(TAG, "setQSTile " + tile); + mTile = tile; + if (mWrapper != null && !mWrapper.setQSTile(tile)) { + handleDeath(); + } + } + + private boolean checkComponentState() { + PackageManager pm = mContext.getPackageManager(); + if (!isPackageAvailable(pm) || !isComponentAvailable(pm)) { + startPackageListening(); + return false; + } + return true; + } + + private void startPackageListening() { + if (DEBUG) Log.d(TAG, "startPackageListening"); + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addDataScheme("package"); + mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler); + mReceiverRegistered = true; + } + + private void stopPackageListening() { + if (DEBUG) Log.d(TAG, "stopPackageListening"); + mContext.unregisterReceiver(this); + mReceiverRegistered = false; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Log.d(TAG, "onReceive: " + intent); + Uri data = intent.getData(); + String pkgName = data.getEncodedSchemeSpecificPart(); + if (!Objects.equal(pkgName, mIntent.getComponent().getPackageName())) { + return; + } + stopPackageListening(); + if (mBound) { + // Trying to bind again will check the state of the package before bothering to bind. + if (DEBUG) Log.d(TAG, "Trying to rebind"); + setBindService(true); + } + } + + private boolean isComponentAvailable(PackageManager pm) { + String packageName = mIntent.getComponent().getPackageName(); + try { + ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(mIntent.getComponent(), + 0, mUser.getIdentifier()); + if (DEBUG && si == null) Log.d(TAG, "Can't find component " + mIntent.getComponent()); + return si != null; + } catch (RemoteException e) { + // Shouldn't happen. + } + return false; + } + + private boolean isPackageAvailable(PackageManager pm) { + String packageName = mIntent.getComponent().getPackageName(); + try { + pm.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier()); + return true; + } catch (PackageManager.NameNotFoundException e) { + if (DEBUG) Log.d(TAG, "Package not available: " + packageName, e); + else Log.d(TAG, "Package not available: " + packageName); + } + return false; + } + + private void queueMessage(int message) { + synchronized (mQueuedMessages) { + mQueuedMessages.add(message); + } + } + + @Override + public void setQSService(IQSService service) { + mService = service; + if (mWrapper == null || !mWrapper.setQSService(service)) { + handleDeath(); + } + } + + @Override + public void onTileAdded() { + if (DEBUG) Log.d(TAG, "onTileAdded"); + if (mWrapper == null || !mWrapper.onTileAdded()) { + queueMessage(MSG_ON_ADDED); + handleDeath(); + } + } + + @Override + public void onTileRemoved() { + if (DEBUG) Log.d(TAG, "onTileRemoved"); + if (mWrapper == null || !mWrapper.onTileRemoved()) { + queueMessage(MSG_ON_REMOVED); + handleDeath(); + } + } + + @Override + public void onStartListening() { + if (DEBUG) Log.d(TAG, "onStartListening"); + mListening = true; + if (mWrapper != null && !mWrapper.onStartListening()) { + handleDeath(); + } + } + + @Override + public void onStopListening() { + if (DEBUG) Log.d(TAG, "onStopListening"); + mListening = false; + if (mWrapper != null && !mWrapper.onStopListening()) { + handleDeath(); + } + } + + @Override + public void onClick(IBinder iBinder) { + if (DEBUG) Log.d(TAG, "onClick " + iBinder); + if (mWrapper == null || !mWrapper.onClick(iBinder)) { + mClickBinder = iBinder; + queueMessage(MSG_ON_CLICK); + handleDeath(); + } + } + + @Override + public IBinder asBinder() { + return mWrapper != null ? mWrapper.asBinder() : null; + } + + @Override + public void binderDied() { + if (DEBUG) Log.d(TAG, "binderDeath"); + handleDeath(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java new file mode 100644 index 000000000000..2f77a303c36c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Handler; +import android.os.UserHandle; +import android.service.quicksettings.IQSTileService; +import android.service.quicksettings.TileService; +import android.support.annotation.VisibleForTesting; +import android.util.Log; + +/** + * Manages the priority which lets {@link TileServices} make decisions about which tiles + * to bind. Also holds on to and manages the {@link TileLifecycleManager}, informing it + * of when it is allowed to bind based on decisions frome the {@link TileServices}. + */ +public class TileServiceManager { + + private static final long MIN_BIND_TIME = 5000; + private static final long UNBIND_DELAY = 30000; + + public static final boolean DEBUG = true; + + private static final String TAG = "TileServiceManager"; + + @VisibleForTesting + static final String PREFS_FILE = "CustomTileModes"; + + private final TileServices mServices; + private final TileLifecycleManager mStateManager; + private final Handler mHandler; + private boolean mBindRequested; + private boolean mBindAllowed; + private boolean mBound; + private int mPriority; + private boolean mJustBound; + private long mLastUpdate; + private int mType; + + TileServiceManager(TileServices tileServices, Handler handler, ComponentName component) { + this(tileServices, handler, new TileLifecycleManager(handler, + tileServices.getContext(), new Intent().setComponent(component), + new UserHandle(ActivityManager.getCurrentUser()))); + } + + @VisibleForTesting + TileServiceManager(TileServices tileServices, Handler handler, + TileLifecycleManager tileLifecycleManager) { + mServices = tileServices; + mHandler = handler; + mStateManager = tileLifecycleManager; + mType = tileServices.getContext().getSharedPreferences(PREFS_FILE, 0) + .getInt(tileLifecycleManager.getComponent().flattenToString(), + TileService.TILE_MODE_UNSET); + mStateManager.setQSService(tileServices); + if (mType == TileService.TILE_MODE_UNSET) { + bindService(); + mStateManager.onTileAdded(); + } + } + + public int getType() { + return mType; + } + + public void setType(int type) { + mServices.getContext().getSharedPreferences(PREFS_FILE, 0).edit() + .putInt(mStateManager.getComponent().flattenToString(), type).commit(); + mType = type; + mServices.recalculateBindAllowance(); + } + + public IQSTileService getTileService() { + return mStateManager; + } + + public void setBindRequested(boolean bindRequested) { + if (mBindRequested == bindRequested) return; + mBindRequested = bindRequested; + if (mBindAllowed && mBindRequested && !mBound) { + mHandler.removeCallbacks(mUnbind); + bindService(); + } else { + mServices.recalculateBindAllowance(); + } + if (mBound && !mBindRequested) { + mHandler.postDelayed(mUnbind, UNBIND_DELAY); + } + } + + public void setLastUpdate(long lastUpdate) { + mLastUpdate = lastUpdate; + if (mBound && mType == TileService.TILE_MODE_ACTIVE) { + mStateManager.onStopListening(); + setBindRequested(false); + } + mServices.recalculateBindAllowance(); + } + + public void handleDestroy() { + mStateManager.handleDestroy(); + } + + public void setBindAllowed(boolean allowed) { + if (mBindAllowed == allowed) return; + mBindAllowed = allowed; + if (!mBindAllowed && mBound) { + unbindService(); + } else if (mBindAllowed && mBindRequested && !mBound) { + bindService(); + } + } + + private void bindService() { + if (mBound) { + Log.e(TAG, "Service already bound"); + return; + } + mBound = true; + mJustBound = true; + mHandler.postDelayed(mJustBoundOver, MIN_BIND_TIME); + mStateManager.setBindService(true); + } + + private void unbindService() { + if (!mBound) { + Log.e(TAG, "Service not bound"); + return; + } + mBound = false; + mJustBound = false; + mStateManager.setBindService(false); + } + + public void calculateBindPriority(long currentTime) { + if (mStateManager.hasPendingClick()) { + // Pending click is the most important thing, need to put this service at the top of + // the list to be bound. + mPriority = Integer.MAX_VALUE; + } else if (mJustBound) { + // If we just bound, lets not thrash on binding/unbinding too much, this is second most + // important. + mPriority = Integer.MAX_VALUE - 1; + } else if (!mBindRequested) { + // Don't care about binding right now, put us last. + mPriority = Integer.MIN_VALUE; + } else { + // Order based on whether this was just updated. + long timeSinceUpdate = currentTime - mLastUpdate; + // Fit compare into integer space for simplicity. Make sure to leave MAX_VALUE and + // MAX_VALUE - 1 for the more important states above. + if (timeSinceUpdate > Integer.MAX_VALUE - 2) { + mPriority = Integer.MAX_VALUE - 2; + } else { + mPriority = (int) timeSinceUpdate; + } + } + } + + public int getBindPriority() { + return mPriority; + } + + private final Runnable mUnbind = new Runnable() { + @Override + public void run() { + if (mBound && !mBindRequested) { + unbindService(); + } + } + }; + + @VisibleForTesting + final Runnable mJustBoundOver = new Runnable() { + @Override + public void run() { + mJustBound = false; + mServices.recalculateBindAllowance(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java new file mode 100644 index 000000000000..00f76990c1e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.service.quicksettings.IQSService; +import android.service.quicksettings.Tile; +import android.service.quicksettings.TileService; +import android.util.ArrayMap; +import android.util.Log; +import com.android.systemui.statusbar.phone.QSTileHost; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * Runs the day-to-day operations of which tiles should be bound and when. + */ +public class TileServices extends IQSService.Stub { + static final int DEFAULT_MAX_BOUND = 3; + static final int REDUCED_MAX_BOUND = 1; + + private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>(); + private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>(); + private final Context mContext; + private final Handler mHandler; + private final QSTileHost mHost; + + private int mMaxBound = DEFAULT_MAX_BOUND; + + public TileServices(QSTileHost host, Looper looper) { + mHost = host; + mContext = mHost.getContext(); + mContext.registerReceiver(mRequestListeningReceiver, + new IntentFilter(TileService.ACTION_REQUEST_LISTENING)); + mHandler = new Handler(looper); + } + + public Context getContext() { + return mContext; + } + + public TileServiceManager getTileWrapper(CustomTile tile) { + ComponentName component = tile.getComponent(); + TileServiceManager service = onCreateTileService(component); + synchronized (mServices) { + mServices.put(tile, service); + mTiles.put(component, tile); + } + return service; + } + + protected TileServiceManager onCreateTileService(ComponentName component) { + return new TileServiceManager(this, mHandler, component); + } + + public void freeService(CustomTile tile, TileServiceManager service) { + synchronized (mServices) { + service.setBindAllowed(false); + mServices.remove(tile); + mTiles.remove(tile.getComponent()); + } + } + + public void setMemoryPressure(boolean memoryPressure) { + mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND; + recalculateBindAllowance(); + } + + public void recalculateBindAllowance() { + final ArrayList<TileServiceManager> services; + synchronized (mServices) { + services = new ArrayList<>(mServices.values()); + } + final int N = services.size(); + if (N > mMaxBound) { + long currentTime = System.currentTimeMillis(); + // Precalculate the priority of services for binding. + for (int i = 0; i < N; i++) { + services.get(i).calculateBindPriority(currentTime); + } + // Sort them so we can bind the most important first. + Collections.sort(services, SERVICE_SORT); + } + int i; + // Allow mMaxBound items to bind. + for (i = 0; i < mMaxBound && i < N; i++) { + services.get(i).setBindAllowed(true); + } + // The rest aren't allowed to bind for now. + while (i < N) { + services.get(i).setBindAllowed(false); + i++; + } + } + + private void verifyCaller(String packageName) { + try { + int uid = mContext.getPackageManager().getPackageUid(packageName, + Binder.getCallingUserHandle().getIdentifier()); + if (Binder.getCallingUid() != uid) { + throw new SecurityException("Component outside caller's uid"); + } + } catch (PackageManager.NameNotFoundException e) { + throw new SecurityException(e); + } + } + + private void requestListening(ComponentName component) { + synchronized (mServices) { + CustomTile customTile = getTileForComponent(component); + if (customTile == null) { + Log.d("TileServices", "Couldn't find tile for " + component); + return; + } + TileServiceManager service = mServices.get(customTile); + if (service.getType() != TileService.TILE_MODE_ACTIVE) { + return; + } + service.setBindRequested(true); + try { + service.getTileService().onStartListening(); + } catch (RemoteException e) { + } + } + } + + @Override + public void setTileMode(ComponentName component, int mode) { + verifyCaller(component.getPackageName()); + CustomTile customTile = getTileForComponent(component); + if (customTile != null) { + synchronized (mServices) { + mServices.get(customTile).setType(mode); + } + } + } + + @Override + public void updateQsTile(Tile tile) { + verifyCaller(tile.getComponentName().getPackageName()); + CustomTile customTile = getTileForComponent(tile.getComponentName()); + if (customTile != null) { + synchronized (mServices) { + mServices.get(customTile).setLastUpdate(System.currentTimeMillis()); + } + customTile.updateState(tile); + customTile.refreshState(); + } + } + + @Override + public void onShowDialog(Tile tile) { + verifyCaller(tile.getComponentName().getPackageName()); + CustomTile customTile = getTileForComponent(tile.getComponentName()); + if (customTile != null) { + customTile.onDialogShown(); + mHost.collapsePanels(); + } + } + + private CustomTile getTileForComponent(ComponentName component) { + synchronized (mServices) { + return mTiles.get(component); + } + } + + private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (TileService.ACTION_REQUEST_LISTENING.equals(intent.getAction())) { + requestListening( + (ComponentName) intent.getParcelableExtra(TileService.EXTRA_COMPONENT)); + } + } + }; + + private static final Comparator<TileServiceManager> SERVICE_SORT = + new Comparator<TileServiceManager>() { + @Override + public int compare(TileServiceManager left, TileServiceManager right) { + return -Integer.compare(left.getBindPriority(), right.getBindPriority()); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index fc802dde9797..c696f886e33f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -58,8 +58,6 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { public void handleClick() { MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); setEnabled(!mState.value); - mEnable.setAllowAnimation(true); - mDisable.setAllowAnimation(true); } private void setEnabled(boolean enabled) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index f73ee354443d..23a15b9d68ab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -71,8 +71,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { protected void handleClick() { MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); mSetting.setValue(mState.value ? 0 : 1); - mEnable.setAllowAnimation(true); - mDisable.setAllowAnimation(true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index d96f735301e5..4f9f46df7d26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -107,8 +107,6 @@ public class DndTile extends QSTile<QSTile.BooleanState> { Toast.LENGTH_LONG).show(); return; } - mDisable.setAllowAnimation(true); - mDisableTotalSilence.setAllowAnimation(true); MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); if (mState.value) { mController.setZen(Global.ZEN_MODE_OFF, null, TAG); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index 12c12980b065..39d9da12d4a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -64,7 +64,7 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements } MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); boolean newState = !mState.value; - refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE); + refreshState(newState); mFlashlightController.setFlashlight(newState); } @@ -73,8 +73,8 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements // TODO: Flashlight available handling... // state.visible = mFlashlightController.isAvailable(); state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label); - if (arg instanceof UserBoolean) { - boolean value = ((UserBoolean) arg).value; + if (arg instanceof Boolean) { + boolean value = (Boolean) arg; if (value == state.value) { return; } @@ -83,7 +83,6 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements state.value = mFlashlightController.isEnabled(); } final AnimationIcon icon = state.value ? mEnable : mDisable; - icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated); state.icon = icon; int onOrOffId = state.value ? R.string.accessibility_quick_settings_flashlight_on @@ -107,12 +106,12 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements @Override public void onFlashlightChanged(boolean enabled) { - refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE); + refreshState(enabled); } @Override public void onFlashlightError() { - refreshState(UserBoolean.BACKGROUND_FALSE); + refreshState(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 250d5674bb6e..55aa32bb0db9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -59,8 +59,6 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { final boolean isEnabled = (Boolean) mState.value; MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled); mController.setHotspotEnabled(!isEnabled); - mEnable.setAllowAnimation(true); - mDisable.setAllowAnimation(true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 08540f69ff6e..e79aabfb8bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -67,8 +67,6 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { mHost.openPanels(); MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled); mController.setLocationEnabled(!wasEnabled); - mEnable.setAllowAnimation(true); - mDisable.setAllowAnimation(true); } }); return; @@ -76,8 +74,6 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { final boolean wasEnabled = (Boolean) mState.value; MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled); mController.setLocationEnabled(!wasEnabled); - mEnable.setAllowAnimation(true); - mDisable.setAllowAnimation(true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index d85cf605c851..7bce54b33d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -63,15 +63,14 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); final boolean newState = !mState.value; mController.setRotationLocked(newState); - refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE); + refreshState(newState); } @Override protected void handleUpdateState(BooleanState state, Object arg) { if (mController == null) return; - final boolean rotationLocked = arg != null ? ((UserBoolean) arg).value + final boolean rotationLocked = arg != null ? (Boolean) arg : mController.isRotationLocked(); - final boolean userInitiated = arg != null ? ((UserBoolean) arg).userInitiated : false; // TODO: Handle accessibility rotation lock and whatnot. // state.visible = mController.isRotationLockAffordanceVisible(); if (state.value == rotationLocked && state.contentDescription != null) { @@ -80,18 +79,15 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { } state.value = rotationLocked; final boolean portrait = isCurrentOrientationLockPortrait(); - final AnimationIcon icon; if (rotationLocked) { final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label : R.string.quick_settings_rotation_locked_landscape_label; state.label = mContext.getString(label); - icon = portrait ? mAutoToPortrait : mAutoToLandscape; + state.icon = portrait ? mAutoToPortrait : mAutoToLandscape; } else { state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); - icon = portrait ? mPortraitToAuto : mLandscapeToAuto; + state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto; } - icon.setAllowAnimation(userInitiated); - state.icon = icon; state.contentDescription = getAccessibilityString(rotationLocked, R.string.accessibility_rotation_lock_on_portrait, R.string.accessibility_rotation_lock_on_landscape, @@ -145,8 +141,7 @@ public class RotationLockTile extends QSTile<QSTile.BooleanState> { private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() { @Override public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { - refreshState(rotationLocked ? UserBoolean.BACKGROUND_TRUE - : UserBoolean.BACKGROUND_FALSE); + refreshState(rotationLocked); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index 07915f8dc56e..255f29f285c4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -93,12 +93,7 @@ public class WorkModeTile extends QSTile<QSTile.BooleanState> { } private void refreshQuietModeState(boolean backgroundRefresh) { - if (backgroundRefresh) { - refreshState(isWorkModeEnabled() ? UserBoolean.BACKGROUND_TRUE - : UserBoolean.BACKGROUND_FALSE); - } else { - refreshState(isWorkModeEnabled() ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE); - } + refreshState(isWorkModeEnabled()); } @Override @@ -108,28 +103,22 @@ public class WorkModeTile extends QSTile<QSTile.BooleanState> { return; } - final boolean userInitialized; - if (arg instanceof UserBoolean) { - state.value = ((UserBoolean) arg).value; - userInitialized = ((UserBoolean) arg).userInitiated; + if (arg instanceof Boolean) { + state.value = (Boolean) arg; } else { state.value = isWorkModeEnabled(); - userInitialized = false; } - final AnimationIcon icon; state.label = mContext.getString(R.string.quick_settings_work_mode_label); if (state.value) { - icon = mEnable; + state.icon = mEnable; state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_work_mode_on); } else { - icon = mDisable; + state.icon = mDisable; state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_work_mode_off); } - icon.setAllowAnimation(userInitialized); - state.icon = icon; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index ffcc805c4e74..57074df392db 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -77,6 +77,7 @@ import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.views.RecentsView; import com.android.systemui.recents.views.SystemBarScrimViews; import com.android.systemui.recents.views.ViewAnimation; +import com.android.systemui.statusbar.BaseStatusBar; import java.util.ArrayList; @@ -298,12 +299,23 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD */ void dismissRecentsToHome(boolean animated) { if (animated) { - ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(null, - mFinishLaunchHomeRunnable, null); + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(); + exitTrigger.increment(); + exitTrigger.addLastDecrementRunnable(mFinishLaunchHomeRunnable); + exitTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + Recents.getSystemServices().sendCloseSystemWindows( + BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); + } + }); mRecentsView.startExitToHomeAnimation( new ViewAnimation.TaskViewExitContext(exitTrigger)); + exitTrigger.decrement(); } else { mFinishLaunchHomeRunnable.run(); + Recents.getSystemServices().sendCloseSystemWindows( + BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); } } @@ -343,7 +355,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); // Initialize the widget host (the host id is static and does not change) - if (!RecentsDebugFlags.Static.DisableSearchBar) { + if (RecentsDebugFlags.Static.EnableSearchBar) { mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID); } mPackageMonitor = new RecentsPackageMonitor(); @@ -368,14 +380,14 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent); // Bind the search app widget when we first start up - if (!RecentsDebugFlags.Static.DisableSearchBar) { + if (RecentsDebugFlags.Static.EnableSearchBar) { mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost); } // Register the broadcast receiver to handle messages when the screen is turned off IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); - if (!RecentsDebugFlags.Static.DisableSearchBar) { + if (RecentsDebugFlags.Static.EnableSearchBar) { filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); } registerReceiver(mSystemBroadcastReceiver, filter); @@ -475,7 +487,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mPackageMonitor.unregister(); // Stop listening for widget package changes if there was one bound - if (!RecentsDebugFlags.Static.DisableSearchBar) { + if (RecentsDebugFlags.Static.EnableSearchBar) { mAppWidgetHost.stopListening(); } @@ -656,8 +668,8 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD ReferenceCountedTrigger t = new ReferenceCountedTrigger(); ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t); ctx.postAnimationTrigger.increment(); - if (mSearchWidgetInfo != null) { - if (!RecentsDebugFlags.Static.DisableSearchBar) { + if (RecentsDebugFlags.Static.EnableSearchBar) { + if (mSearchWidgetInfo != null) { ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 7547570fadb2..cfbd1cb13ab9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -74,9 +74,6 @@ public class RecentsConfiguration { public int svelteLevel; public int searchBarSpaceHeightPx; - /** Dev options and global settings */ - public boolean lockToAppEnabled; - public RecentsConfiguration(Context context) { // Load only resources that can not change after the first load either through developer // settings or via multi window @@ -106,12 +103,7 @@ public class RecentsConfiguration { /** * Updates the configuration based on the current state of the system */ - void update(Context context, SystemServicesProxy ssp, Rect windowRect) { - // Only update resources that can change after the first load, either through developer - // settings or via multi window - lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() && - ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0; - + void update(Rect windowRect) { // Recompute some values based on the given state, since we can not rely on the resource // system to get certain values. boolean isLandscape = windowRect.width() > windowRect.height(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java index 40c84baf7fd6..c323522013d4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java @@ -32,8 +32,8 @@ public class RecentsDebugFlags implements TunerService.Tunable { public static class Static { // Enables debug drawing for the transition thumbnail public static final boolean EnableTransitionThumbnailDebugMode = false; - // This disables the search bar integration - public static final boolean DisableSearchBar = true; + // This enables the search bar integration + public static final boolean EnableSearchBar = false; // This disables the bitmap and icon caches public static final boolean DisableBackgroundCache = false; // Enables the simulated task affiliations diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 949fb86d281b..fd00289b0248 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -19,6 +19,7 @@ package com.android.systemui.recents; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ITaskStackListener; +import android.appwidget.AppWidgetProviderInfo; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -60,6 +61,7 @@ import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; +import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.util.ArrayList; @@ -104,6 +106,10 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements mHandler.post(this); } + @Override + public void onActivityPinned() { + } + /** Preloads the next task */ public void run() { // TODO: Temporarily skip this if multi stack is enabled @@ -360,6 +366,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements // Otherwise, start the recents activity startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */); + + // Only close the other system windows if we are actually showing recents + ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS); mLastToggleTime = SystemClock.elapsedRealtime(); } } catch (ActivityNotFoundException e) { @@ -572,9 +581,9 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements Rect windowRect = ssp.getWindowRect(); // Update the configuration for the current state - config.update(mContext, ssp, ssp.getWindowRect()); + config.update(windowRect); - if (!RecentsDebugFlags.Static.DisableSearchBar && tryAndBindSearchWidget) { + if (RecentsDebugFlags.Static.EnableSearchBar && tryAndBindSearchWidget) { // Try and pre-emptively bind the search widget on startup to ensure that we // have the right thumbnail bounds to animate to. // Note: We have to reload the widget id before we get the task stack bounds below @@ -850,11 +859,19 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements if (!useThumbnailTransition) { // If there is no thumbnail transition, but is launching from home into recents, then // use a quick home transition and do the animation from home - if (!RecentsDebugFlags.Static.DisableSearchBar && hasRecentTasks) { + if (hasRecentTasks) { SystemServicesProxy ssp = Recents.getSystemServices(); String homeActivityPackage = ssp.getHomeActivityPackageName(); - String searchWidgetPackage = Prefs.getString(mContext, - Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null); + String searchWidgetPackage = null; + if (RecentsDebugFlags.Static.EnableSearchBar) { + searchWidgetPackage = Prefs.getString(mContext, + Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null); + } else { + AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget(); + if (searchWidgetInfo != null) { + searchWidgetPackage = searchWidgetInfo.provider.getPackageName(); + } + } // Determine whether we are coming from a search owned home activity boolean fromSearchHome = (homeActivityPackage != null) && diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 35e53f69a151..108029d68805 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -58,7 +58,7 @@ import android.util.Log; import android.util.MutableBoolean; import android.util.Pair; import android.view.Display; -import android.view.IDockDividerVisibilityListener; +import android.view.IDockedStackListener; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; @@ -68,6 +68,7 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.RecentsImpl; +import com.android.systemui.statusbar.BaseStatusBar; import java.io.IOException; import java.util.ArrayList; @@ -79,6 +80,7 @@ import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; /** * Acts as a shim around the real system services that we need to access data from, and provides @@ -128,7 +130,9 @@ public class SystemServicesProxy { mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); mHasFreeformWorkspaceSupport = - mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT); + mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) || + Settings.Global.getInt(context.getContentResolver(), + DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; // Get the dummy thumbnail width/heights Resources res = context.getResources(); @@ -310,7 +314,8 @@ public class SystemServicesProxy { try { final ActivityOptions options = ActivityOptions.makeBasic(); options.setDockCreateMode(createMode); - mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, options.toBundle()); + options.setLaunchStackId(DOCKED_STACK_ID); + mIam.startActivityFromRecents(taskId, options.toBundle()); } catch (RemoteException e) { e.printStackTrace(); } @@ -498,6 +503,18 @@ public class SystemServicesProxy { } /** + * Sends a message to close other system windows. + */ + public void sendCloseSystemWindows(String reason) { + if (ActivityManagerNative.isSystemReady()) { + try { + ActivityManagerNative.getDefault().closeSystemDialogs(reason); + } catch (RemoteException e) { + } + } + } + + /** * Returns the activity info for a given component name. * * @param cn The component name of the activity. @@ -634,7 +651,7 @@ public class SystemServicesProxy { if (mPm == null) return null; if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null; - ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); + ArrayList<ResolveInfo> homeActivities = new ArrayList<>(); ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities); if (defaultHomeActivity != null) { return defaultHomeActivity.getPackageName(); @@ -722,7 +739,7 @@ public class SystemServicesProxy { /** * Returns the first Recents widget from the same package as the global assist activity. */ - private AppWidgetProviderInfo resolveSearchAppWidget() { + public AppWidgetProviderInfo resolveSearchAppWidget() { if (mAssistComponent == null) return null; List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders( AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); @@ -836,8 +853,7 @@ public class SystemServicesProxy { ActivityOptions options) { if (mIam != null) { try { - mIam.startActivityFromRecents( - taskId, INVALID_STACK_ID, options == null ? null : options.toBundle()); + mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle()); return true; } catch (Exception e) { Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); @@ -879,12 +895,11 @@ public class SystemServicesProxy { } } - public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) { + public void registerDockedStackListener(IDockedStackListener listener) { if (mWm == null) return; try { - WindowManagerGlobal.getWindowManagerService().registerDockDividerVisibilityListener( - listener); + WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener); } catch (Exception e) { e.printStackTrace(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 7a92b2ae0a21..d6262ac3da44 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -155,7 +155,7 @@ public class RecentsTaskLoadPlan { // Add the task to the stack Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon, thumbnail, title, contentDescription, activityColor, !isStackTask, - (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription); + t.bounds, t.taskDescription); allTasks.add(task); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 34a0e5256652..d030fc1a906e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -131,8 +131,6 @@ public class Task { */ public boolean isLaunchTarget; public boolean isHistorical; - public boolean lockToThisTask; - public boolean lockToTaskEnabled; private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>(); @@ -142,8 +140,8 @@ public class Task { public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon, Bitmap thumbnail, String title, String contentDescription, int colorPrimary, - boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled, - Rect bounds, ActivityManager.TaskDescription taskDescription) { + boolean isHistorical, Rect bounds, + ActivityManager.TaskDescription taskDescription) { boolean isInAffiliationGroup = (affiliationTaskId != key.id); boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0); this.key = key; @@ -159,8 +157,6 @@ public class Task { this.bounds = bounds; this.taskDescription = taskDescription; this.isHistorical = isHistorical; - this.lockToThisTask = lockToTaskEnabled && lockToThisTask; - this.lockToTaskEnabled = lockToTaskEnabled; } /** Copies the other task. */ @@ -178,8 +174,6 @@ public class Task { this.bounds = o.bounds; this.isLaunchTarget = o.isLaunchTarget; this.isHistorical = o.isHistorical; - this.lockToThisTask = o.lockToThisTask; - this.lockToTaskEnabled = o.lockToTaskEnabled; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 6f003ab63282..5e720cba36c7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -427,8 +427,6 @@ public class TaskStack { removeGroup(group); } } - // Update the lock-to-app state - t.lockToThisTask = false; } /** Removes a task */ @@ -437,9 +435,6 @@ public class TaskStack { boolean wasFrontMostTask = (getStackFrontMostTask() == t); removeTaskImpl(mStackTaskList, t); Task newFrontMostTask = getStackFrontMostTask(); - if (newFrontMostTask != null && newFrontMostTask.lockToTaskEnabled) { - newFrontMostTask.lockToThisTask = true; - } if (mCb != null) { // Notify that a task has been removed mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask); @@ -524,10 +519,21 @@ public class TaskStack { mAffinitiesGroups.clear(); } - /** Gets the front task */ + /** + * Gets the front-most task in the stack. + */ public Task getStackFrontMostTask() { - if (mStackTaskList.size() == 0) return null; - return mStackTaskList.getTasks().get(mStackTaskList.size() - 1); + ArrayList<Task> stackTasks = mStackTaskList.getTasks(); + if (stackTasks.isEmpty()) { + return null; + } + for (int i = stackTasks.size() - 1; i >= 0; i--) { + Task task = stackTasks.get(i); + if (!task.isFreeformTask()) { + return task; + } + } + return null; } /** Gets the task keys */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 9b1315a2220f..c95c73bc4f89 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -317,7 +317,7 @@ public class RecentsView extends FrameLayout { * Hides the task stack and shows the empty view. */ public void showEmptyView() { - if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) { + if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) { mSearchBar.setVisibility(View.INVISIBLE); } mTaskStackView.setVisibility(View.INVISIBLE); @@ -332,7 +332,7 @@ public class RecentsView extends FrameLayout { public void hideEmptyView() { mEmptyView.setVisibility(View.INVISIBLE); mTaskStackView.setVisibility(View.VISIBLE); - if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) { + if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) { mSearchBar.setVisibility(View.VISIBLE); } mTaskStackView.bringToFront(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 10df15609e0f..9d391b0c8c86 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -333,7 +333,6 @@ public class TaskStackLayoutAlgorithm { * including the search bar. */ public void initialize(Rect taskStackBounds, StackState state) { - RecentsDebugFlags debugFlags = Recents.getDebugFlags(); RecentsConfiguration config = Recents.getConfiguration(); int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width()); int heightPadding = mContext.getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 830d60799717..94fae1381d85 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Parcelable; +import android.provider.Settings; import android.util.IntProperty; import android.util.Log; import android.util.Property; @@ -149,7 +150,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal List<TaskView> mImmutableTaskViews = new ArrayList<>(); List<TaskView> mTmpTaskViews = new ArrayList<>(); LayoutInflater mInflater; + boolean mTouchExplorationEnabled; + boolean mScreenPinningEnabled; Interpolator mFastOutSlowInInterpolator; @@ -224,6 +227,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal protected void onAttachedToWindow() { SystemServicesProxy ssp = Recents.getSystemServices(); mTouchExplorationEnabled = ssp.isTouchExplorationEnabled(); + mScreenPinningEnabled = ssp.getSystemSetting(getContext(), + Settings.System.LOCK_TO_APP_ENABLED) != 0; EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); super.onAttachedToWindow(); } @@ -1023,8 +1028,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal launchTargetTask); hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask(); } - tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget, - offscreenY); + tv.prepareEnterRecentsAnimation(hideTask, occludesLaunchTarget, offscreenY); } // If the enter animation started already and we haven't completed a layout yet, do the @@ -1087,6 +1091,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect; ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) && launchTargetTask.group.isTaskAboveTask(task, launchTargetTask); + ctx.isScreenPinningEnabled = mScreenPinningEnabled; ctx.updateListener = mRequestUpdateClippingListener; mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null); @@ -1271,13 +1276,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION); } - // Update the new front most task - if (newFrontMostTask != null) { + // Update the new front most task's action button + if (mScreenPinningEnabled && newFrontMostTask != null) { TaskView frontTv = getChildViewForTask(newFrontMostTask); if (frontTv != null) { - frontTv.onTaskBound(newFrontMostTask); - frontTv.fadeInActionButton(getResources().getInteger( - R.integer.recents_task_enter_from_app_duration)); + frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION); } } @@ -1304,7 +1307,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void prepareViewToEnterPool(TaskView tv) { - Task task = tv.getTask(); + final Task task = tv.getTask(); // Report that this tasks's data is no longer being used Recents.getTaskLoader().unloadTaskData(task); @@ -1314,14 +1317,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Update the task views list after removing the task view updateTaskViewsList(); - // Reset the view properties + // Reset the view properties and view state tv.resetViewProperties(); - - // Reset the focused view state tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */); - - // Reset the clip state of the task view tv.setClipViewInStack(false); + if (mScreenPinningEnabled) { + tv.hideActionButton(); + } } @Override @@ -1362,6 +1364,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (mFocusedTask == task) { tv.setFocusedState(true, false /* animated */, false /* requestViewFocus */); } + + // Restore the action button visibility if it is the front most task view + if (mScreenPinningEnabled && tv.getTask() == mStack.getStackFrontMostTask()) { + tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index a3e8b2d72d56..14909c50ef94 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -138,6 +138,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, setBackground(new FakeShadowDrawable(res, config)); } setOutlineProvider(mViewBounds); + setOnLongClickListener(this); } /** Set callback */ @@ -175,8 +176,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, public void getOutline(View view, Outline outline) { // Set the outline to match the FAB background outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight()); + outline.setAlpha(0.35f); } }); + mActionButtonView.setOnClickListener(this); mActionButtonTranslationZ = mActionButtonView.getTranslationZ(); } @@ -293,8 +296,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Prepares this task view for the enter-recents animations. This is called earlier in the * first layout because the actual animation into recents may take a long time. */ - void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean hideTask, - boolean occludesLaunchTarget, int offscreenY) { + void prepareEnterRecentsAnimation(boolean hideTask, boolean occludesLaunchTarget, + int offscreenY) { RecentsConfiguration config = Recents.getConfiguration(); RecentsActivityLaunchState launchState = config.getLaunchState(); int initialDim = getDim(); @@ -303,7 +306,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } else if (launchState.launchedHasConfigurationChanged) { // Just load the views as-is } else if (launchState.launchedFromAppWithThumbnail) { - if (isTaskViewLaunchTargetTask) { + if (mTask.isLaunchTarget) { // Set the dim to 0 so we can animate it in initialDim = 0; // Hide the action button @@ -342,11 +345,15 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, if (launchState.launchedFromAppWithThumbnail) { if (mTask.isLaunchTarget) { ctx.postAnimationTrigger.increment(); - // Immediately start the dim animation + // Start the dim animation animateDimToProgress(taskViewEnterFromAppDuration, ctx.postAnimationTrigger.decrementOnAnimationEnd()); - // Animate the action button in - fadeInActionButton(taskViewEnterFromAppDuration); + + // Start the action button animation + if (ctx.isScreenPinningEnabled) { + showActionButton(true /* fadeIn */, + taskViewEnterFromAppDuration /* fadeInDuration */); + } } else { // Animate the task up if it was occluding the launch target if (ctx.currentTaskOccludesLaunchTarget) { @@ -413,17 +420,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, animate().cancel(); } - public void fadeInActionButton(int duration) { - // Hide the action button - mActionButtonView.setAlpha(0f); - - // Animate the action button in - mActionButtonView.animate().alpha(1f) - .setDuration(duration) - .setInterpolator(PhoneStatusBar.ALPHA_IN) - .start(); - } - /** Animates this task view as it leaves recents by pressing home. */ void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) { int taskViewExitToHomeDuration = getResources().getInteger( @@ -468,8 +464,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, .alpha(0f) .setStartDelay(0) .setDuration(taskViewExitToAppDuration) - .setInterpolator(mFastOutLinearInInterpolator) + .setInterpolator(PhoneStatusBar.ALPHA_OUT) .withEndAction(postAnimRunnable) + .withLayer() .start(); } else { // Hide the dismiss button @@ -644,8 +641,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, setDim(getDimFromTaskProgress()); } - /**** View focus state ****/ - /** * Explicitly sets the focused state of this task. */ @@ -672,19 +667,42 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } } + /** + * Shows the action button. + * @param fadeIn whether or not to animate the action button in. + * @param fadeInDuration the duration of the action button animation, only used if + * {@param fadeIn} is true. + */ + public void showActionButton(boolean fadeIn, int fadeInDuration) { + mActionButtonView.setVisibility(View.VISIBLE); + + if (fadeIn) { + if (mActionButtonView.getAlpha() < 1f) { + mActionButtonView.setAlpha(0f); + mActionButtonView.animate().alpha(1f) + .setDuration(fadeInDuration) + .setInterpolator(PhoneStatusBar.ALPHA_IN) + .withLayer() + .start(); + } + } else { + mActionButtonView.setAlpha(1f); + } + } + + /** + * Immediately hides the action button. + */ + public void hideActionButton() { + mActionButtonView.setVisibility(View.INVISIBLE); + } + /**** TaskCallbacks Implementation ****/ /** Binds this task view to the task */ public void onTaskBound(Task t) { mTask = t; mTask.addCallback(this); - - // Hide the action button if lock to app is disabled for this view - int lockButtonVisibility = (!t.lockToTaskEnabled || !t.lockToThisTask) ? GONE : VISIBLE; - if (mActionButtonView.getVisibility() != lockButtonVisibility) { - mActionButtonView.setVisibility(lockButtonVisibility); - requestLayout(); - } } @Override @@ -693,10 +711,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // Bind each of the views to the new task data mThumbnailView.rebindToTask(mTask); mHeaderView.rebindToTask(mTask); - - // Rebind any listeners - mActionButtonView.setOnClickListener(this); - setOnLongClickListener(this); } mTaskDataLoaded = true; } @@ -708,8 +722,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mTask.removeCallback(this); mThumbnailView.unbindFromTask(); mHeaderView.unbindFromTask(); - // Unbind any listeners - mActionButtonView.setOnClickListener(null); } mTaskDataLoaded = false; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java index e1d80fdcfa68..eaef51c4869a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java @@ -33,6 +33,8 @@ public class ViewAnimation { // These following properties are updated for each task view we start the enter animation on + // Whether or not screen pinning is enabled + boolean isScreenPinningEnabled; // Whether or not the current task occludes the launch target boolean currentTaskOccludesLaunchTarget; // The task rect for the current stack diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 6ff7a3e08159..189e651b7dca 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -17,7 +17,8 @@ package com.android.systemui.stackdivider; import android.content.res.Configuration; -import android.view.IDockDividerVisibilityListener; +import android.os.RemoteException; +import android.view.IDockedStackListener; import android.view.LayoutInflater; import android.view.View; @@ -49,7 +50,7 @@ public class Divider extends SystemUI { putComponent(Divider.class, this); mDockDividerVisibilityListener = new DockDividerVisibilityListener(); SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.registerDockDividerVisibilityListener(mDockDividerVisibilityListener); + ssp.registerDockedStackListener(mDockDividerVisibilityListener); } @Override @@ -94,10 +95,15 @@ public class Divider extends SystemUI { }); } - class DockDividerVisibilityListener extends IDockDividerVisibilityListener.Stub { + class DockDividerVisibilityListener extends IDockedStackListener.Stub { + @Override - public void onDockDividerVisibilityChanged(boolean visible) { + public void onDividerVisibilityChanged(boolean visible) throws RemoteException { updateVisibility(visible); } + + @Override + public void onDockedStackExistsChanged(boolean exists) throws RemoteException { + } } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java new file mode 100644 index 000000000000..5ef56f3312dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.stackdivider; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.Property; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.widget.ImageButton; + +import com.android.systemui.R; + +/** + * View for the handle in the docked stack divider. + */ +public class DividerHandleView extends ImageButton { + + private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY + = new Property<DividerHandleView, Integer>(Integer.class, "width") { + + @Override + public Integer get(DividerHandleView object) { + return object.mCurrentWidth; + } + + @Override + public void set(DividerHandleView object, Integer value) { + object.mCurrentWidth = value; + object.invalidate(); + } + }; + + private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY + = new Property<DividerHandleView, Integer>(Integer.class, "height") { + + @Override + public Integer get(DividerHandleView object) { + return object.mCurrentHeight; + } + + @Override + public void set(DividerHandleView object, Integer value) { + object.mCurrentHeight = value; + object.invalidate(); + } + }; + + private final Paint mPaint = new Paint(); + private final int mWidth; + private final int mHeight; + private final int mCircleDiameter; + private final Interpolator mFastOutSlowInInterpolator; + private int mCurrentWidth; + private int mCurrentHeight; + private AnimatorSet mAnimator; + + public DividerHandleView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null)); + mPaint.setAntiAlias(true); + mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width); + mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height); + mCurrentWidth = mWidth; + mCurrentHeight = mHeight; + mCircleDiameter = (mWidth + mHeight) / 3; + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in); + } + + public void setTouching(boolean touching, boolean animate) { + if (mAnimator != null) { + mAnimator.cancel(); + mAnimator = null; + } + if (!animate) { + if (touching) { + mCurrentWidth = mCircleDiameter; + mCurrentHeight = mCircleDiameter; + } else { + mCurrentWidth = mWidth; + mCurrentHeight = mHeight; + } + invalidate(); + } else { + animateToTarget(touching ? mCircleDiameter : mWidth, + touching ? mCircleDiameter : mHeight, touching); + } + } + + private void animateToTarget(int targetWidth, int targetHeight, boolean touching) { + ObjectAnimator widthAnimator = ObjectAnimator.ofInt(this, WIDTH_PROPERTY, + mCurrentWidth, targetWidth); + ObjectAnimator heightAnimator = ObjectAnimator.ofInt(this, HEIGHT_PROPERTY, + mCurrentHeight, targetHeight); + mAnimator = new AnimatorSet(); + mAnimator.playTogether(widthAnimator, heightAnimator); + mAnimator.setDuration(touching + ? DividerView.TOUCH_ANIMATION_DURATION + : DividerView.TOUCH_RELEASE_ANIMATION_DURATION); + mAnimator.setInterpolator(touching + ? DividerView.TOUCH_RESPONSE_INTERPOLATOR + : mFastOutSlowInInterpolator); + mAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimator = null; + } + }); + mAnimator.start(); + } + + @Override + protected void onDraw(Canvas canvas) { + int left = getWidth() / 2 - mCurrentWidth / 2; + int top = getHeight() / 2 - mCurrentHeight / 2; + int radius = Math.min(mCurrentWidth, mCurrentHeight) / 2; + canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight, + radius, radius, mPaint); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java index 5af172c7e59a..e43d531c7a5c 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java @@ -17,25 +17,42 @@ package com.android.systemui.stackdivider; import android.content.Context; -import android.util.DisplayMetrics; -import android.view.DisplayInfo; +import android.graphics.Rect; import com.android.systemui.statusbar.FlingAnimationUtils; import java.util.ArrayList; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; - /** * Calculates the snap targets and the snap position given a position and a velocity. All positions * here are to be interpreted as the left/top edge of the divider rectangle. */ public class DividerSnapAlgorithm { + /** + * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio + */ + private static final int SNAP_MODE_16_9 = 0; + + /** + * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) + */ + private static final int SNAP_FIXED_RATIO = 1; + + /** + * 1 snap target: 1:1 + */ + private static final int SNAP_ONLY_1_1 = 2; + private final Context mContext; private final FlingAnimationUtils mFlingAnimationUtils; + private final int mDisplayWidth; + private final int mDisplayHeight; private final int mDividerSize; - private final ArrayList<SnapTarget> mTargets; + private final ArrayList<SnapTarget> mTargets = new ArrayList<>(); + private final Rect mInsets = new Rect(); + private final int mSnapMode; + private final float mFixedRatio; /** The first target which is still splitting the screen */ private final SnapTarget mFirstSplitTarget; @@ -47,11 +64,19 @@ public class DividerSnapAlgorithm { private final SnapTarget mDismissEndTarget; public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils, - int dividerSize, boolean isHorizontalDivision) { + int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision, + Rect insets) { mContext = ctx; mFlingAnimationUtils = flingAnimationUtils; mDividerSize = dividerSize; - mTargets = calculateTargets(isHorizontalDivision); + mDisplayWidth = displayWidth; + mDisplayHeight = displayHeight; + mInsets.set(insets); + mSnapMode = ctx.getResources().getInteger( + com.android.internal.R.integer.config_dockedStackDividerSnapMode); + mFixedRatio = ctx.getResources().getFraction( + com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1); + calculateTargets(isHorizontalDivision); mFirstSplitTarget = mTargets.get(1); mLastSplitTarget = mTargets.get(mTargets.size() - 2); mDismissStartTarget = mTargets.get(0); @@ -75,6 +100,40 @@ public class DividerSnapAlgorithm { } } + public float calculateDismissingFraction(int position) { + if (position < mFirstSplitTarget.position) { + return 1f - (float) position / mFirstSplitTarget.position; + } else if (position > mLastSplitTarget.position) { + return (float) (position - mLastSplitTarget.position) + / (mDismissEndTarget.position - mLastSplitTarget.position); + } + return 0f; + } + + public SnapTarget getClosestDismissTarget(int position) { + if (position - mDismissStartTarget.position < mDismissEndTarget.position - position) { + return mDismissStartTarget; + } else { + return mDismissEndTarget; + } + } + + public SnapTarget getFirstSplitTarget() { + return mFirstSplitTarget; + } + + public SnapTarget getLastSplitTarget() { + return mLastSplitTarget; + } + + public SnapTarget getDismissStartTarget() { + return mDismissStartTarget; + } + + public SnapTarget getDismissEndTarget() { + return mDismissEndTarget; + } + private SnapTarget snap(int position) { int minIndex = -1; int minDistance = Integer.MAX_VALUE; @@ -89,22 +148,61 @@ public class DividerSnapAlgorithm { return mTargets.get(minIndex); } - private ArrayList<SnapTarget> calculateTargets(boolean isHorizontalDivision) { - ArrayList<SnapTarget> targets = new ArrayList<>(); - DisplayMetrics info = mContext.getResources().getDisplayMetrics(); + private void calculateTargets(boolean isHorizontalDivision) { + mTargets.clear(); int dividerMax = isHorizontalDivision - ? info.heightPixels - : info.widthPixels; + ? mDisplayHeight + : mDisplayWidth; + mTargets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START)); + switch (mSnapMode) { + case SNAP_MODE_16_9: + addRatio16_9Targets(isHorizontalDivision); + break; + case SNAP_FIXED_RATIO: + addFixedDivisionTargets(isHorizontalDivision); + break; + case SNAP_ONLY_1_1: + addMiddleTarget(isHorizontalDivision); + break; + } + mTargets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END)); + } - // TODO: Better calculation - targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START)); - targets.add(new SnapTarget((int) (0.3415f * dividerMax) - mDividerSize / 2, + private void addFixedDivisionTargets(boolean isHorizontalDivision) { + int start = isHorizontalDivision ? mInsets.top : mInsets.left; + int end = isHorizontalDivision + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; + mTargets.add(new SnapTarget((int) (start + mFixedRatio * (end - start)) - mDividerSize / 2, SnapTarget.FLAG_NONE)); - targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE)); - targets.add(new SnapTarget((int) (0.6585f * dividerMax) - mDividerSize / 2, + addMiddleTarget(isHorizontalDivision); + mTargets.add(new SnapTarget((int) (start + (1 - mFixedRatio) * (end - start)) + - mDividerSize / 2, SnapTarget.FLAG_NONE)); + } + + private void addRatio16_9Targets(boolean isHorizontalDivision) { + int start = isHorizontalDivision ? mInsets.top : mInsets.left; + int end = isHorizontalDivision + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; + int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; + int endOther = isHorizontalDivision + ? mDisplayWidth - mInsets.right + : mDisplayHeight - mInsets.bottom; + float size = 9.0f / 16.0f * (endOther - startOther); + int sizeInt = (int) Math.floor(size); + mTargets.add(new SnapTarget(start + sizeInt, SnapTarget.FLAG_NONE)); + addMiddleTarget(isHorizontalDivision); + mTargets.add(new SnapTarget(end - sizeInt - mDividerSize, SnapTarget.FLAG_NONE)); + } + + private void addMiddleTarget(boolean isHorizontalDivision) { + int start = isHorizontalDivision ? mInsets.top : mInsets.left; + int end = isHorizontalDivision + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; + mTargets.add(new SnapTarget(start + (end - start) / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE)); - targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END)); - return targets; } /** diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 13642ebddb17..109cf4726101 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -21,12 +21,14 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; +import android.app.ActivityManager.StackId; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region.Op; import android.hardware.display.DisplayManager; import android.util.AttributeSet; +import android.util.MathUtils; import android.view.Display; import android.view.DisplayInfo; import android.view.MotionEvent; @@ -34,8 +36,11 @@ import android.view.PointerIcon; import android.view.VelocityTracker; import android.view.View; import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; import android.view.ViewTreeObserver.InternalInsetsInfo; import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; +import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -56,15 +61,29 @@ import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW; public class DividerView extends FrameLayout implements OnTouchListener, OnComputeInternalInsetsListener { + static final long TOUCH_ANIMATION_DURATION = 150; + static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; + static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = + new PathInterpolator(0.3f, 0f, 0.1f, 1f); + private static final String TAG = "DividerView"; - private ImageButton mHandle; + private static final int TASK_POSITION_SAME = Integer.MAX_VALUE; + private static final float DIM_START_FRACTION = 0.5f; + private static final float DIM_DAMP_FACTOR = 1.7f; + + private static final PathInterpolator SLOWDOWN_INTERPOLATOR = + new PathInterpolator(0.5f, 1f, 0.5f, 1f); + + private DividerHandleView mHandle; private View mBackground; private int mStartX; private int mStartY; private int mStartPosition; private int mDockSide; private final int[] mTempInt2 = new int[2]; + private boolean mMoving; + private int mTouchSlop; private int mDividerInsets; private int mDisplayWidth; @@ -73,15 +92,20 @@ public class DividerView extends FrameLayout implements OnTouchListener, private int mDividerSize; private int mTouchElevation; - private final Rect mTmpRect = new Rect(); + private final Rect mDockedRect = new Rect(); + private final Rect mDockedTaskRect = new Rect(); + private final Rect mOtherTaskRect = new Rect(); + private final Rect mOtherRect = new Rect(); + private final Rect mDockedInsetRect = new Rect(); + private final Rect mOtherInsetRect = new Rect(); private final Rect mLastResizeRect = new Rect(); private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance(); private Interpolator mFastOutSlowInInterpolator; - private final Interpolator mTouchResponseInterpolator = - new PathInterpolator(0.3f, 0f, 0.1f, 1f); private DividerWindowManager mWindowManager; private VelocityTracker mVelocityTracker; private FlingAnimationUtils mFlingAnimationUtils; + private DividerSnapAlgorithm mSnapAlgorithm; + private final Rect mStableInsets = new Rect(); public DividerView(Context context) { super(context); @@ -103,7 +127,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onFinishInflate() { super.onFinishInflate(); - mHandle = (ImageButton) findViewById(R.id.docked_divider_handle); + mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle); mBackground = findViewById(R.id.docked_divider_background); mHandle.setOnTouchListener(this); mDividerWindowWidth = getResources().getDimensionPixelSize( @@ -115,6 +139,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, R.dimen.docked_stack_divider_lift_elevation); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); + mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f); updateDisplayInfo(); boolean landscape = getResources().getConfiguration().orientation @@ -124,6 +149,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, getViewTreeObserver().addOnComputeInternalInsetsListener(this); } + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), + insets.getStableInsetRight(), insets.getStableInsetBottom()); + return super.onApplyWindowInsets(insets); + } + public void setWindowManager(DividerWindowManager windowManager) { mWindowManager = windowManager; } @@ -132,8 +164,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, return mWindowManagerProxy; } - public boolean startDragging() { + public boolean startDragging(boolean animate) { + mHandle.setTouching(true, animate); mDockSide = mWindowManagerProxy.getDockSide(); + mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDisplayWidth, + mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); if (mDockSide != WindowManager.DOCKED_INVALID) { mWindowManagerProxy.setResizing(true); mWindowManager.setSlippery(false); @@ -145,11 +180,16 @@ public class DividerView extends FrameLayout implements OnTouchListener, } public void stopDragging(int position, float velocity) { + mHandle.setTouching(false, true /* animate */); fling(position, velocity); mWindowManager.setSlippery(true); releaseBackground(); } + public DividerSnapAlgorithm getSnapAlgorithm() { + return mSnapAlgorithm; + } + @Override public boolean onTouch(View v, MotionEvent event) { convertToScreenCoordinates(event); @@ -161,19 +201,31 @@ public class DividerView extends FrameLayout implements OnTouchListener, mStartX = (int) event.getX(); mStartY = (int) event.getY(); getLocationOnScreen(mTempInt2); - boolean result = startDragging(); + boolean result = startDragging(true /* animate */); if (isHorizontalDivision()) { mStartPosition = mTempInt2[1] + mDividerInsets; } else { mStartPosition = mTempInt2[0] + mDividerInsets; } + mMoving = false; return result; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); - if (mDockSide != WindowManager.DOCKED_INVALID) { - resizeStack(calculatePosition(x, y)); + boolean exceededTouchSlop = + isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop + || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop); + if (!mMoving && exceededTouchSlop) { + mStartX = x; + mStartY = y; + mMoving = true; + } + if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { + int position = calculatePosition(x, y); + SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, + 0 /* velocity */); + resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget); } break; case MotionEvent.ACTION_UP: @@ -187,6 +239,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, int position = calculatePosition(x, y); stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity() : mVelocityTracker.getXVelocity()); + mMoving = false; break; } return true; @@ -197,14 +250,16 @@ public class DividerView extends FrameLayout implements OnTouchListener, } private void fling(int position, float velocity) { - final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, - mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity); + final SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity); ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - resizeStack((Integer) animation.getAnimatedValue()); + resizeStack((Integer) animation.getAnimatedValue(), + animation.getAnimatedFraction() == 1f + ? TASK_POSITION_SAME + : snapTarget.position, snapTarget); } }); anim.addListener(new AnimatorListenerAdapter() { @@ -236,6 +291,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } else { mWindowManagerProxy.maximizeDockedStack(); } + mWindowManagerProxy.setResizeDimLayer(false, -1, 0f); } private void liftBackground() { @@ -245,29 +301,33 @@ public class DividerView extends FrameLayout implements OnTouchListener, mBackground.animate().scaleX(1.4f); } mBackground.animate() - .setInterpolator(mTouchResponseInterpolator) - .setDuration(150) - .translationZ(mTouchElevation); + .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) + .setDuration(TOUCH_ANIMATION_DURATION) + .translationZ(mTouchElevation) + .start(); // Lift handle as well so it doesn't get behind the background, even though it doesn't // cast shadow. mHandle.animate() - .setInterpolator(mTouchResponseInterpolator) - .setDuration(150) - .translationZ(mTouchElevation); + .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) + .setDuration(TOUCH_ANIMATION_DURATION) + .translationZ(mTouchElevation) + .start(); } private void releaseBackground() { mBackground.animate() .setInterpolator(mFastOutSlowInInterpolator) - .setDuration(200) + .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) .translationZ(0) .scaleX(1f) - .scaleY(1f); + .scaleY(1f) + .start(); mHandle.animate() .setInterpolator(mFastOutSlowInInterpolator) - .setDuration(200) - .translationZ(0); + .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) + .translationZ(0) + .start(); } @Override @@ -332,17 +392,184 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public void resizeStack(int position) { - calculateBoundsForPosition(position, mDockSide, mTmpRect); - if (mTmpRect.equals(mLastResizeRect)) { + private int invertDockSide(int dockSide) { + switch (dockSide) { + case WindowManager.DOCKED_LEFT: + return WindowManager.DOCKED_RIGHT; + case WindowManager.DOCKED_TOP: + return WindowManager.DOCKED_BOTTOM; + case WindowManager.DOCKED_RIGHT: + return WindowManager.DOCKED_LEFT; + case WindowManager.DOCKED_BOTTOM: + return WindowManager.DOCKED_TOP; + default: + return WindowManager.DOCKED_INVALID; + } + } + + private void alignTopLeft(Rect containingRect, Rect rect) { + int width = rect.width(); + int height = rect.height(); + rect.set(containingRect.left, containingRect.top, + containingRect.left + width, containingRect.top + height); + } + + private void alignBottomRight(Rect containingRect, Rect rect) { + int width = rect.width(); + int height = rect.height(); + rect.set(containingRect.right - width, containingRect.bottom - height, + containingRect.right, containingRect.bottom); + } + + public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { + calculateBoundsForPosition(position, mDockSide, mDockedRect); + + if (mDockedRect.equals(mLastResizeRect)) { return; } // Make sure shadows are updated mBackground.invalidate(); - mLastResizeRect.set(mTmpRect); - mWindowManagerProxy.resizeDockedStack(mTmpRect); + mLastResizeRect.set(mDockedRect); + if (taskPosition != TASK_POSITION_SAME) { + calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect); + int dockSideInverted = invertDockSide(mDockSide); + int taskPositionDocked = + restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget); + int taskPositionOther = + restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget); + calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect); + calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect); + alignTopLeft(mDockedRect, mDockedTaskRect); + alignTopLeft(mOtherRect, mOtherTaskRect); + mDockedInsetRect.set(mDockedTaskRect); + mOtherInsetRect.set(mOtherTaskRect); + if (dockSideTopLeft(mDockSide)) { + alignTopLeft(mDockedRect, mDockedInsetRect); + alignBottomRight(mOtherRect, mOtherInsetRect); + } else { + alignBottomRight(mDockedRect, mDockedInsetRect); + alignTopLeft(mOtherRect, mOtherInsetRect); + } + applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position, + taskPositionDocked); + applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position, + taskPositionOther); + mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect, + mOtherTaskRect, mOtherInsetRect); + } else { + mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null); + } + float fraction = mSnapAlgorithm.calculateDismissingFraction(position); + fraction = Math.max(0, + Math.min((fraction / DIM_START_FRACTION - 1f) / DIM_DAMP_FACTOR, 1f)); + mWindowManagerProxy.setResizeDimLayer(fraction != 0f, + getStackIdForDismissTarget(mSnapAlgorithm.getClosestDismissTarget(position)), + fraction); + } + + /** + * When the snap target is dismissing one side, make sure that the dismissing side doesn't get + * 0 size. + */ + private int restrictDismissingTaskPosition(int taskPosition, int dockSide, + SnapTarget snapTarget) { + if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) { + return mSnapAlgorithm.getFirstSplitTarget().position; + } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END + && dockSideBottomRight(dockSide)) { + return mSnapAlgorithm.getLastSplitTarget().position; + } else { + return taskPosition; + } + } + + /** + * Applies a parallax to the task when dismissing. + */ + private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget, + int position, int taskPosition) { + float fraction = Math.min(1, Math.max(0, + mSnapAlgorithm.calculateDismissingFraction(position))); + SnapTarget dismissTarget = null; + SnapTarget splitTarget = null; + if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START + || snapTarget == mSnapAlgorithm.getFirstSplitTarget()) + && dockSideTopLeft(dockSide)) { + dismissTarget = mSnapAlgorithm.getDismissStartTarget(); + splitTarget = mSnapAlgorithm.getFirstSplitTarget(); + } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END + || snapTarget == mSnapAlgorithm.getLastSplitTarget()) + && dockSideBottomRight(dockSide)) { + dismissTarget = mSnapAlgorithm.getDismissEndTarget(); + splitTarget = mSnapAlgorithm.getLastSplitTarget(); + } + if (dismissTarget != null && fraction > 0f + && isDismissing(splitTarget, position, dockSide)) { + fraction = calculateParallaxDismissingFraction(fraction); + int offsetPosition = (int) (taskPosition + + fraction * (dismissTarget.position - splitTarget.position)); + int width = taskRect.width(); + int height = taskRect.height(); + switch (dockSide) { + case WindowManager.DOCKED_LEFT: + taskRect.left = offsetPosition - width; + taskRect.right = offsetPosition; + break; + case WindowManager.DOCKED_RIGHT: + taskRect.left = offsetPosition + mDividerSize; + taskRect.right = offsetPosition + width + mDividerSize; + break; + case WindowManager.DOCKED_TOP: + taskRect.top = offsetPosition - height; + taskRect.bottom = offsetPosition; + break; + case WindowManager.DOCKED_BOTTOM: + taskRect.top = offsetPosition + mDividerSize; + taskRect.bottom = offsetPosition + height + mDividerSize; + break; + } + } + } + + /** + * @return for a specified {@code fraction}, this returns an adjusted value that simulates a + * slowing down parallax effect + */ + private static float calculateParallaxDismissingFraction(float fraction) { + return SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f; + } + + private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) { + if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) { + return position < snapTarget.position; + } else { + return position > snapTarget.position; + } + } + + private int getStackIdForDismissTarget(SnapTarget dismissTarget) { + if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && + (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) { + return StackId.DOCKED_STACK_ID; + } else { + return StackId.FULLSCREEN_WORKSPACE_STACK_ID; + } + } + + /** + * @return true if and only if {@code dockSide} is top or left + */ + private static boolean dockSideTopLeft(int dockSide) { + return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT; + } + + /** + * @return true if and only if {@code dockSide} is bottom or right + */ + private static boolean dockSideBottomRight(int dockSide) { + return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java index 225187455f3f..161f873267d0 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java @@ -50,6 +50,9 @@ public class DividerWindowManager { | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); mLp.setTitle(WINDOW_TITLE); + view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); mWindowManager.addView(view, mLp); mView = view; } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index ef47d8d1f2d1..2791dfc3972e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -40,18 +40,41 @@ public class WindowManagerProxy { private static final WindowManagerProxy sInstance = new WindowManagerProxy(); @GuardedBy("mResizeRect") - private final Rect mResizeRect = new Rect(); - private final Rect mTmpRect = new Rect(); + private final Rect mDockedRect = new Rect(); + private final Rect mTempDockedTaskRect = new Rect(); + private final Rect mTempDockedInsetRect = new Rect(); + private final Rect mTempOtherTaskRect = new Rect(); + private final Rect mTempOtherInsetRect = new Rect(); + + private final Rect mTmpRect1 = new Rect(); + private final Rect mTmpRect2 = new Rect(); + private final Rect mTmpRect3 = new Rect(); + private final Rect mTmpRect4 = new Rect(); + private final Rect mTmpRect5 = new Rect(); + + private boolean mDimLayerVisible; + private int mDimLayerTargetStack; + private float mDimLayerAlpha; + private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private final Runnable mResizeRunnable = new Runnable() { @Override public void run() { - synchronized (mResizeRect) { - mTmpRect.set(mResizeRect); + synchronized (mDockedRect) { + mTmpRect1.set(mDockedRect); + mTmpRect2.set(mTempDockedTaskRect); + mTmpRect3.set(mTempDockedInsetRect); + mTmpRect4.set(mTempOtherTaskRect); + mTmpRect5.set(mTempOtherInsetRect); } try { - ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, mTmpRect, true); + ActivityManagerNative.getDefault() + .resizeDockedStack(mTmpRect1, + mTmpRect2.isEmpty() ? null : mTmpRect2, + mTmpRect3.isEmpty() ? null : mTmpRect3, + mTmpRect4.isEmpty() ? null : mTmpRect4, + mTmpRect5.isEmpty() ? null : mTmpRect5); } catch (RemoteException e) { Log.w(TAG, "Failed to resize stack: " + e); } @@ -80,6 +103,18 @@ public class WindowManagerProxy { } }; + private final Runnable mDimLayerRunnable = new Runnable() { + @Override + public void run() { + try { + WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible, + mDimLayerTargetStack, mDimLayerAlpha); + } catch (RemoteException e) { + Log.w(TAG, "Failed to resize stack: " + e); + } + } + }; + private WindowManagerProxy() { } @@ -87,9 +122,30 @@ public class WindowManagerProxy { return sInstance; } - public void resizeDockedStack(Rect rect) { - synchronized (mResizeRect) { - mResizeRect.set(rect); + public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect, + Rect tempOtherTaskRect, Rect tempOtherInsetRect) { + synchronized (mDockedRect) { + mDockedRect.set(docked); + if (tempDockedTaskRect != null) { + mTempDockedTaskRect.set(tempDockedTaskRect); + } else { + mTempDockedTaskRect.setEmpty(); + } + if (tempDockedInsetRect != null) { + mTempDockedInsetRect.set(tempDockedInsetRect); + } else { + mTempDockedInsetRect.setEmpty(); + } + if (tempOtherTaskRect != null) { + mTempOtherTaskRect.set(tempOtherTaskRect); + } else { + mTempOtherTaskRect.setEmpty(); + } + if (tempOtherInsetRect != null) { + mTempOtherInsetRect.set(tempOtherInsetRect); + } else { + mTempOtherInsetRect.setEmpty(); + } } mExecutor.execute(mResizeRunnable); } @@ -123,4 +179,11 @@ public class WindowManagerProxy { } return DOCKED_INVALID; } + + public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + mDimLayerVisible = visible; + mDimLayerTargetStack = targetStackId; + mDimLayerAlpha = alpha; + mExecutor.execute(mDimLayerRunnable); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 1d7651c462e8..5906bdac4523 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -148,8 +148,9 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int INTERRUPTION_THRESHOLD = 10; protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; - // Should match the value in PhoneWindowManager + // Should match the values in PhoneWindowManager public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; + public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; private static final String BANNER_ACTION_CANCEL = "com.android.systemui.statusbar.banner_action_cancel"; @@ -970,7 +971,7 @@ public abstract class BaseStatusBar extends SystemUI implements ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon); ((TextView) row.findViewById(R.id.pkgname)).setText(appname); - final View settingsButton = guts.findViewById(R.id.notification_inspect_item); + final View settingsButton = guts.findViewById(R.id.more_settings); if (appUid >= 0) { final int appUidF = appUid; settingsButton.setOnClickListener(new View.OnClickListener() { @@ -983,6 +984,13 @@ public abstract class BaseStatusBar extends SystemUI implements settingsButton.setVisibility(View.GONE); } + row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismissPopups(); + } + }); + guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey())); } @@ -1182,7 +1190,6 @@ public abstract class BaseStatusBar extends SystemUI implements protected void toggleRecents() { if (mRecents != null) { - sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS); mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index f7680a7d81b2..3cc1ab9b42ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -292,6 +292,15 @@ public class NotificationData { return false; } + public boolean shouldSuppressScreenOn(String key) { + if (mRankingMap != null) { + mRankingMap.getRanking(key, mTmpRanking); + return (mTmpRanking.getSuppressedVisualEffects() + & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0; + } + return false; + } + public int getImportance(String key) { if (mRankingMap != null) { mRankingMap.getRanking(key, mTmpRanking); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java index 58c9722f4af6..d0c14f1fd52b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java @@ -746,7 +746,7 @@ class NavigationBarApps extends LinearLayout // Launch or bring the activity to front. final IActivityManager iAm = ActivityManagerNative.getDefault(); try { - iAm.startActivityFromRecents(taskPersistentId, INVALID_STACK_ID, null /* options */); + iAm.startActivityFromRecents(taskPersistentId, null /* options */); } catch (RemoteException e) { Slog.e(TAG, "Exception when activating a recent task", e); } catch (IllegalArgumentException e) { @@ -1104,6 +1104,10 @@ class NavigationBarApps extends LinearLayout } }); } + + @Override + public void onActivityPinned() { + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 79bd626efdb6..cc85d0fb8a25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -29,6 +29,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget; +import com.android.systemui.stackdivider.DividerView; import com.android.systemui.tuner.TunerService; import static android.view.WindowManager.*; @@ -189,7 +191,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, createMode, initialBounds); if (mDragMode == DRAG_MODE_DIVIDER) { - mDivider.getView().startDragging(); + mDivider.getView().startDragging(false /* animate */); } mDockWindowTouchSlopExceeded = true; MetricsLogger.action(mContext, @@ -198,8 +200,10 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL } } else { if (mDragMode == DRAG_MODE_DIVIDER) { - mDivider.getView().resizeStack( - !mIsVertical ? (int) event.getRawY() : (int) event.getRawX()); + int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX(); + SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm() + .calculateSnapTarget(position, 0f /* velocity */); + mDivider.getView().resizeStack(position, snapTarget.position, snapTarget); } else if (mDragMode == DRAG_MODE_RECENTS) { mRecentsComponent.onDraggingInRecents(event.getRawY()); } 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 cddb1fc3a6ad..a3d0ce69bdd6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -36,11 +36,13 @@ import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.IDockedStackListener.Stub; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -227,8 +229,8 @@ public class NavigationBarView extends LinearLayout { return mCurrentView; } - public View getRecentsButton() { - return mCurrentView.findViewById(R.id.recent_apps); + public KeyButtonView getRecentsButton() { + return (KeyButtonView) mCurrentView.findViewById(R.id.recent_apps); } public View getMenuButton() { @@ -455,6 +457,32 @@ public class NavigationBarView extends LinearLayout { getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); updateRTLOrder(); + + try { + WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() { + @Override + public void onDividerVisibilityChanged(boolean visible) throws RemoteException { + } + + @Override + public void onDockedStackExistsChanged(final boolean exists) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + updateRecentsIcon(exists); + } + }); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Failed registering docked stack exists listener", e); + } + } + + private void updateRecentsIcon(boolean dockedStackExists) { + getRecentsButton().setImageResource(dockedStackExists + ? R.drawable.ic_sysbar_docked + : R.drawable.ic_sysbar_recent); } public boolean isVertical() { 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 80fcba60e895..78d09e3e03a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1269,19 +1269,26 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { - // Stop screensaver if the notification has a full-screen intent. - // (like an incoming phone call) - awakenDreams(); + if (mNotificationData.shouldSuppressScreenOn(notification.getKey())) { + if (DEBUG) { + Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey()); + } + } else { + // Stop screensaver if the notification has a full-screen intent. + // (like an incoming phone call) + awakenDreams(); - // not immersive & a full-screen alert should be shown - if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); - try { - EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, - notification.getKey()); - notification.getNotification().fullScreenIntent.send(); - shadeEntry.notifyFullScreenIntentLaunched(); - MetricsLogger.count(mContext, "note_fullscreen", 1); - } catch (PendingIntent.CanceledException e) { + // not immersive & a full-screen alert should be shown + if (DEBUG) + Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); + try { + EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, + notification.getKey()); + notification.getNotification().fullScreenIntent.send(); + shadeEntry.notifyFullScreenIntentLaunched(); + MetricsLogger.count(mContext, "note_fullscreen", 1); + } catch (PendingIntent.CanceledException e) { + } } } addNotificationViews(shadeEntry, ranking); @@ -4120,32 +4127,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return false; } - // Recents - - @Override - protected void showRecents(boolean triggeredFromAltTab) { - // Set the recents visibility flag - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - notifyUiVisibilityChanged(mSystemUiVisibility); - super.showRecents(triggeredFromAltTab); - } - - @Override - protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { - // Unset the recents visibility flag - mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; - notifyUiVisibilityChanged(mSystemUiVisibility); - super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); - } - - @Override - protected void toggleRecents() { - // Toggle the recents visibility flag - mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; - notifyUiVisibilityChanged(mSystemUiVisibility); - super.toggleRecents(); - } - public void updateRecentsVisibility(boolean visible) { // Update the recents visibility flag if (visible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index e6d837a06d4d..5c856e89c6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -18,30 +18,25 @@ package com.android.systemui.statusbar.phone; import android.app.ActivityManager; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.os.Binder; import android.os.HandlerThread; import android.os.Looper; import android.os.Process; -import android.os.RemoteException; import android.provider.Settings; -import android.service.quicksettings.IQSService; -import android.service.quicksettings.Tile; import android.text.TextUtils; import android.util.Log; import com.android.systemui.R; import com.android.systemui.qs.QSTile; +import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.qs.external.TileServices; import com.android.systemui.qs.tiles.AirplaneModeTile; import com.android.systemui.qs.tiles.BatteryTile; import com.android.systemui.qs.tiles.BluetoothTile; import com.android.systemui.qs.tiles.CastTile; import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorInversionTile; -import com.android.systemui.qs.tiles.CustomTile; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; @@ -76,7 +71,7 @@ import java.util.List; import java.util.Map; /** Platform implementation of the quick settings tile host **/ -public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tunable { +public final class QSTileHost implements QSTile.Host, Tunable { private static final String TAG = "QSTileHost"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -100,6 +95,7 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu private final KeyguardMonitor mKeyguard; private final SecurityController mSecurity; private final BatteryController mBattery; + private final TileServices mServices; private final List<Callback> mCallbacks = new ArrayList<>(); @@ -131,6 +127,8 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu ht.start(); mLooper = ht.getLooper(); + mServices = new TileServices(this, mLooper); + TunerService.get(mContext).addTunable(this, TILES_SETTING); } @@ -256,6 +254,10 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu return mSecurity; } + public TileServices getTileServices() { + return mServices; + } + @Override public void onTuningChanged(String key, String newValue) { if (!TILES_SETTING.equals(key)) { @@ -306,50 +308,6 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu TextUtils.join(",", specs), ActivityManager.getCurrentUser()); } - @Override - public void updateQsTile(Tile tile) throws RemoteException { - verifyCaller(tile.getComponentName().getPackageName()); - CustomTile customTile = getTileForComponent(tile.getComponentName()); - if (customTile != null) { - customTile.updateState(tile); - customTile.refreshState(); - } - } - - @Override - public void onShowDialog(Tile tile) throws RemoteException { - verifyCaller(tile.getComponentName().getPackageName()); - CustomTile customTile = getTileForComponent(tile.getComponentName()); - if (customTile != null) { - customTile.onDialogShown(); - collapsePanels(); - } - } - - private void verifyCaller(String packageName) { - try { - int uid = mContext.getPackageManager().getPackageUid(packageName, - Binder.getCallingUserHandle().getIdentifier()); - if (Binder.getCallingUid() != uid) { - throw new SecurityException("Component outside caller's uid"); - } - } catch (NameNotFoundException e) { - throw new SecurityException(e); - } - } - - private CustomTile getTileForComponent(ComponentName component) { - // TODO: Build map for easier lookup. - for (QSTile<?> qsTile : mTiles.values()) { - if (qsTile instanceof CustomTile) { - if (((CustomTile) qsTile).getComponent().equals(component)) { - return (CustomTile) qsTile; - } - } - } - return null; - } - public QSTile<?> createTile(String tileSpec) { if (tileSpec.equals("wifi")) return WifiTile.isSupported(this) ? new WifiTile(this) : null; @@ -374,7 +332,10 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu // Intent tiles. else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec); - else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); + else { + Log.w(TAG, "Bad tile spec: " + tileSpec); + return null; + } } public static List<String> loadTileSpecs(Context context, String tileList) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java index 1372ccaa824e..a91f6a2879bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java @@ -24,7 +24,6 @@ import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -135,7 +134,6 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements @Override public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { mNextAlarm = nextAlarm; - Log.d(TAG, "Got alarm update " + (nextAlarm != null)); if (nextAlarm != null) { mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm)); } @@ -179,10 +177,8 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements private void updateListeners() { if (mListening) { - Log.d(TAG, "Listening for Alarms"); mNextAlarmController.addStateChangedCallback(this); } else { - Log.d(TAG, "Not listening for Alarms"); mNextAlarmController.removeStateChangedCallback(this); } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 1e3b0f1d7b8c..9081af156f00 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -153,8 +153,11 @@ public class TunerService extends SystemUI { private static TunerService sInstance; public static TunerService get(Context context) { - SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext(); - TunerService service = sysUi.getComponent(TunerService.class); + TunerService service = null; + if (context.getApplicationContext() instanceof SystemUIApplication) { + SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext(); + service = sysUi.getComponent(TunerService.class); + } if (service == null) { // Can't get it as a component, must in the tuner, lets just create one for now. return getStaticService(context); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index 9d4ec108aec1..3c63aae9c416 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -648,7 +648,7 @@ public class VolumeDialog { if (D.BUG) Log.d(TAG, "updateFooterH"); final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE; final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF - && mAudioManager.isStreamAffectedByRingerMode(mActiveStream); + && (mAudioManager.isStreamAffectedByRingerMode(mActiveStream) || mExpanded); if (wasVisible != visible && !visible) { prepareForCollapse(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index cd47ac10fbd8..38d8de0d9b15 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -510,7 +510,7 @@ public class ZenModePanel extends LinearLayout { GregorianCalendar weekRange = new GregorianCalendar(); final long now = weekRange.getTimeInMillis(); setToMidnight(weekRange); - weekRange.roll(Calendar.DATE, 6); + weekRange.add(Calendar.DATE, 6); final long nextAlarmMs = mController.getNextAlarm(); if (nextAlarmMs > 0) { GregorianCalendar nextAlarm = new GregorianCalendar(); diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index c21af2457030..282560100cb4 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -19,10 +19,16 @@ <uses-permission android:name="android.permission.INJECT_EVENTS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> <application> <uses-library android:name="android.test.runner" /> <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" /> + + <service + android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService" + android:process=":killable" /> </application> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java new file mode 100644 index 000000000000..6ebf488937f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.external; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.quicksettings.IQSService; +import android.service.quicksettings.IQSTileService; +import android.service.quicksettings.Tile; +import android.test.AndroidTestCase; +import android.util.ArraySet; +import android.util.Log; + +public class TileLifecycleManagerTests extends AndroidTestCase { + public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE"; + public static final String EXTRA_CALLBACK = "callback"; + + private HandlerThread mThread; + private Handler mHandler; + private TileLifecycleManager mStateManager; + private final Object mBroadcastLock = new Object(); + private final ArraySet<String> mCallbacks = new ArraySet<>(); + private boolean mBound; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mThread = new HandlerThread("TestThread"); + mThread.start(); + mHandler = new Handler(mThread.getLooper()); + mStateManager = new TileLifecycleManager(mHandler, getContext(), + new Intent(mContext, FakeTileService.class), new UserHandle(UserHandle.myUserId())); + mCallbacks.clear(); + getContext().registerReceiver(mReceiver, new IntentFilter(TILE_UPDATE_BROADCAST)); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + if (mBound) { + unbindService(); + } + mThread.quit(); + getContext().unregisterReceiver(mReceiver); + } + + public void testSync() { + syncWithHandler(); + } + + public void testBind() { + bindService(); + waitForCallback("onCreate"); + } + + public void testUnbind() { + bindService(); + waitForCallback("onCreate"); + unbindService(); + waitForCallback("onDestroy"); + } + + public void testTileServiceCallbacks() { + bindService(); + waitForCallback("onCreate"); + + mStateManager.onTileAdded(); + waitForCallback("onTileAdded"); + mStateManager.onStartListening(); + waitForCallback("onStartListening"); + mStateManager.onClick(null); + waitForCallback("onClick"); + mStateManager.onStopListening(); + waitForCallback("onStopListening"); + mStateManager.onTileRemoved(); + waitForCallback("onTileRemoved"); + + unbindService(); + } + + public void testAddedBeforeBind() { + mStateManager.onTileAdded(); + + bindService(); + waitForCallback("onCreate"); + waitForCallback("onTileAdded"); + } + + public void testListeningBeforeBind() { + mStateManager.onTileAdded(); + mStateManager.onStartListening(); + + bindService(); + waitForCallback("onCreate"); + waitForCallback("onTileAdded"); + waitForCallback("onStartListening"); + } + + public void testClickBeforeBind() { + mStateManager.onTileAdded(); + mStateManager.onStartListening(); + mStateManager.onClick(null); + + bindService(); + waitForCallback("onCreate"); + waitForCallback("onTileAdded"); + waitForCallback("onStartListening"); + waitForCallback("onClick"); + } + + public void testListeningNotListeningBeforeBind() { + mStateManager.onTileAdded(); + mStateManager.onStartListening(); + mStateManager.onStopListening(); + + bindService(); + waitForCallback("onCreate"); + unbindService(); + waitForCallback("onDestroy"); + assertFalse(mCallbacks.contains("onStartListening")); + } + + public void testNoClickOfNotListeningAnymore() { + mStateManager.onTileAdded(); + mStateManager.onStartListening(); + mStateManager.onClick(null); + mStateManager.onStopListening(); + + bindService(); + waitForCallback("onCreate"); + unbindService(); + waitForCallback("onDestroy"); + assertFalse(mCallbacks.contains("onClick")); + } + + public void testComponentEnabling() { + mStateManager.onTileAdded(); + mStateManager.onStartListening(); + + PackageManager pm = getContext().getPackageManager(); + pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + + bindService(); + assertTrue(mStateManager.mReceiverRegistered); + + pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + waitForCallback("onCreate"); + } + + public void testKillProcess() { + mStateManager.onStartListening(); + bindService(); + waitForCallback("onCreate"); + waitForCallback("onStartListening"); + + getContext().sendBroadcast(new Intent(FakeTileService.ACTION_KILL)); + + waitForCallback("onCreate"); + waitForCallback("onStartListening"); + } + + private void bindService() { + mBound = true; + mStateManager.setBindService(true); + } + + private void unbindService() { + mBound = false; + mStateManager.setBindService(false); + } + + private void waitForCallback(String callback) { + for (int i = 0; i < 25; i++) { + if (mCallbacks.contains(callback)) { + mCallbacks.remove(callback); + return; + } + synchronized (mBroadcastLock) { + try { + mBroadcastLock.wait(500); + } catch (InterruptedException e) { + } + } + } + if (mCallbacks.contains(callback)) { + mCallbacks.remove(callback); + return; + } + fail("Didn't receive callback: " + callback); + } + + private void syncWithHandler() { + final Object lock = new Object(); + synchronized (lock) { + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (lock) { + lock.notify(); + } + } + }); + try { + lock.wait(5000); + } catch (InterruptedException e) { + } + } + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mCallbacks.add(intent.getStringExtra(EXTRA_CALLBACK)); + synchronized (mBroadcastLock) { + mBroadcastLock.notify(); + } + } + }; + + public static class FakeTileService extends Service { + public static final String ACTION_KILL = "com.android.systemui.test.KILL"; + + @Override + public IBinder onBind(Intent intent) { + return new IQSTileService.Stub() { + + @Override + public void setQSService(IQSService service) { + + } + + @Override + public void setQSTile(Tile tile) throws RemoteException { + } + + @Override + public void onTileAdded() throws RemoteException { + sendCallback("onTileAdded"); + } + + @Override + public void onTileRemoved() throws RemoteException { + sendCallback("onTileRemoved"); + } + + @Override + public void onStartListening() throws RemoteException { + sendCallback("onStartListening"); + } + + @Override + public void onStopListening() throws RemoteException { + sendCallback("onStopListening"); + } + + @Override + public void onClick(IBinder iBinder) throws RemoteException { + sendCallback("onClick"); + } + }; + } + + @Override + public void onCreate() { + super.onCreate(); + registerReceiver(mReceiver, new IntentFilter(ACTION_KILL)); + sendCallback("onCreate"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + sendCallback("onDestroy"); + } + + private void sendCallback(String callback) { + Log.d("TileLifecycleManager", "Relaying: " + callback); + sendBroadcast(new Intent(TILE_UPDATE_BROADCAST) + .putExtra(EXTRA_CALLBACK, callback)); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_KILL.equals(intent.getAction())) { + Process.killProcess(Process.myPid()); + } + } + }; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java new file mode 100644 index 000000000000..4586c2813f72 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.content.ComponentName; +import android.os.Handler; +import android.os.HandlerThread; +import android.service.quicksettings.TileService; +import com.android.systemui.SysuiTestCase; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +public class TileServiceManagerTests extends SysuiTestCase { + + private TileServices mTileServices; + private TileLifecycleManager mTileLifecycle; + private HandlerThread mThread; + private Handler mHandler; + private TileServiceManager mTileServiceManager; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mThread = new HandlerThread("TestThread"); + mThread.start(); + mHandler = new Handler(mThread.getLooper()); + mTileServices = Mockito.mock(TileServices.class); + Mockito.when(mTileServices.getContext()).thenReturn(mContext); + mTileLifecycle = Mockito.mock(TileLifecycleManager.class); + ComponentName componentName = new ComponentName(mContext, + TileServiceManagerTests.class); + Mockito.when(mTileLifecycle.getComponent()).thenReturn(componentName); + mContext.getSharedPreferences(TileServiceManager.PREFS_FILE, 0).edit() + .putInt(componentName.flattenToString(), TileService.TILE_MODE_PASSIVE).commit(); + mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mTileLifecycle); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mThread.quit(); + } + + public void testSetBindRequested() { + // Request binding. + mTileServiceManager.setBindRequested(true); + mTileServiceManager.setLastUpdate(0); + mTileServiceManager.calculateBindPriority(5); + Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance(); + assertEquals(5, mTileServiceManager.getBindPriority()); + + // Verify same state doesn't trigger recalculating for no reason. + mTileServiceManager.setBindRequested(true); + Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance(); + + mTileServiceManager.setBindRequested(false); + mTileServiceManager.calculateBindPriority(5); + Mockito.verify(mTileServices, Mockito.times(3)).recalculateBindAllowance(); + assertEquals(Integer.MIN_VALUE, mTileServiceManager.getBindPriority()); + } + + public void testPendingClickPriority() { + Mockito.when(mTileLifecycle.hasPendingClick()).thenReturn(true); + mTileServiceManager.calculateBindPriority(0); + assertEquals(Integer.MAX_VALUE, mTileServiceManager.getBindPriority()); + } + + public void testBind() { + // Trigger binding requested and allowed. + mTileServiceManager.setBindRequested(true); + mTileServiceManager.setBindAllowed(true); + + ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mTileLifecycle, Mockito.times(1)).setBindService(captor.capture()); + assertTrue((boolean) captor.getValue()); + + mTileServiceManager.setBindRequested(false); + mTileServiceManager.calculateBindPriority(0); + // Priority shouldn't disappear after the request goes away if we just bound, instead + // it sticks around to avoid thrashing a bunch of processes. + assertEquals(Integer.MAX_VALUE - 1, mTileServiceManager.getBindPriority()); + + mTileServiceManager.setBindAllowed(false); + captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mTileLifecycle, Mockito.times(2)).setBindService(captor.capture()); + assertFalse((boolean) captor.getValue()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java new file mode 100644 index 000000000000..7a3ce878b8cc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.qs.external; + +import android.content.ComponentName; +import android.os.Looper; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.phone.QSTileHost; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.util.ArrayList; + +public class TileServicesTests extends SysuiTestCase { + private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2; + + private TileServices mTileService; + private ArrayList<TileServiceManager> mManagers; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mManagers = new ArrayList<>(); + QSTileHost host = new QSTileHost(mContext, null, null, null, null, null, null, null, null, + null, null, null, null, null, null); + mTileService = new TestTileServices(host, Looper.myLooper()); + } + + public void testRecalculateBindAllowance() { + // Add some fake tiles. + for (int i = 0; i < NUM_FAKES; i++) { + mTileService.getTileWrapper(Mockito.mock(CustomTile.class)); + } + assertEquals(NUM_FAKES, mManagers.size()); + + for (int i = 0; i < NUM_FAKES; i++) { + Mockito.when(mManagers.get(i).getBindPriority()).thenReturn(i); + } + mTileService.recalculateBindAllowance(); + for (int i = 0; i < NUM_FAKES; i++) { + Mockito.verify(mManagers.get(i), Mockito.times(1)).calculateBindPriority( + Mockito.anyLong()); + ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture()); + + assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.DEFAULT_MAX_BOUND), + (boolean) captor.getValue()); + } + } + + public void testSetMemoryPressure() { + testRecalculateBindAllowance(); + mTileService.setMemoryPressure(true); + + for (int i = 0; i < NUM_FAKES; i++) { + ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mManagers.get(i), Mockito.times(2)).setBindAllowed(captor.capture()); + + assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.REDUCED_MAX_BOUND), + (boolean) captor.getValue()); + } + } + + public void testCalcFew() { + for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) { + mTileService.getTileWrapper(Mockito.mock(CustomTile.class)); + } + mTileService.recalculateBindAllowance(); + + for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) { + // Shouldn't get bind prioirities calculated when there are less than the max services. + Mockito.verify(mManagers.get(i), Mockito.never()).calculateBindPriority( + Mockito.anyLong()); + + // All should be bound since there are less than the max services. + ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture()); + + assertTrue(captor.getValue()); + } + } + + private class TestTileServices extends TileServices { + public TestTileServices(QSTileHost host, Looper looper) { + super(host, looper); + } + + @Override + protected TileServiceManager onCreateTileService(ComponentName component) { + TileServiceManager manager = Mockito.mock(TileServiceManager.class); + mManagers.add(manager); + return manager; + } + } +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3e8dc4e1ba18..617169482ec4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -244,6 +244,7 @@ import libcore.util.EmptyArray; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; @@ -257,6 +258,7 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEBUG_APP; +import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; @@ -1445,6 +1447,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int SYSTEM_USER_UNLOCK_MSG = 61; static final int LOG_STACK_STATE = 62; static final int VR_MODE_CHANGE_MSG = 63; + static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1998,9 +2001,7 @@ public final class ActivityManagerService extends ActivityManagerNative } case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG: { synchronized (ActivityManagerService.this) { - int i = mTaskStackListeners.beginBroadcast(); - while (i > 0) { - i--; + for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) { try { // Make a one-way callback to the listener mTaskStackListeners.getBroadcastItem(i).onTaskStackChanged(); @@ -2012,6 +2013,20 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } + case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG: { + synchronized (ActivityManagerService.this) { + for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) { + try { + // Make a one-way callback to the listener + mTaskStackListeners.getBroadcastItem(i).onActivityPinned(); + } catch (RemoteException e){ + // Handled by the RemoteCallbackList + } + } + mTaskStackListeners.finishBroadcast(); + } + break; + } case NOTIFY_CLEARTEXT_NETWORK_MSG: { final int uid = msg.arg1; final byte[] firstPacket = (byte[]) msg.obj; @@ -2743,9 +2758,15 @@ public final class ActivityManagerService extends ActivityManagerNative return mAppBindArgs; } - final void setFocusedActivityLocked(ActivityRecord r, String reason) { + boolean setFocusedActivityLocked(ActivityRecord r, String reason) { if (r == null || mFocusedActivity == r) { - return; + return false; + } + + if (!r.isFocusable()) { + if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, + "setFocusedActivityLocked: unfocusable r=" + r); + return false; } if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r); @@ -2783,7 +2804,7 @@ public final class ActivityManagerService extends ActivityManagerNative finishVoiceTask(last.task.voiceSession); } } - if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) { + if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) { mWindowManager.setFocusedApp(r.appToken, true); } applyUpdateLockStateLocked(r); @@ -2799,23 +2820,35 @@ public final class ActivityManagerService extends ActivityManagerNative mFocusedActivity == null ? -1 : mFocusedActivity.userId, mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName, reason); + return true; } - final void clearFocusedActivity(ActivityRecord r) { - if (mFocusedActivity == r) { - ActivityStack stack = mStackSupervisor.getFocusedStack(); - if (stack != null) { - ActivityRecord top = stack.topActivity(); - if (top != null && top.userId != mLastFocusedUserId) { - mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); - mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, - top.userId, 0)); - mLastFocusedUserId = top.userId; - } + final void resetFocusedActivityIfNeededLocked(ActivityRecord goingAway) { + if (mFocusedActivity != goingAway) { + return; + } + + final ActivityStack focusedStack = mStackSupervisor.getFocusedStack(); + if (focusedStack != null) { + final ActivityRecord top = focusedStack.topActivity(); + if (top != null && top.userId != mLastFocusedUserId) { + mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); + mHandler.sendMessage( + mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, top.userId, 0)); + mLastFocusedUserId = top.userId; } - mFocusedActivity = null; - EventLogTags.writeAmFocusedActivity(-1, "NULL", "clearFocusedActivity"); } + + // Try to move focus to another activity if possible. + if (setFocusedActivityLocked( + focusedStack.topRunningActivityLocked(), "resetFocusedActivityIfNeeded")) { + return; + } + + if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "resetFocusedActivityIfNeeded: Setting focus to NULL " + + "prev mFocusedActivity=" + mFocusedActivity + " goingAway=" + goingAway); + mFocusedActivity = null; + EventLogTags.writeAmFocusedActivity(-1, "NULL", "resetFocusedActivityIfNeeded"); } @Override @@ -2827,7 +2860,7 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord r = stack.topRunningActivityLocked(); if (r != null) { setFocusedActivityLocked(r, "setFocusedStack"); - mStackSupervisor.resumeTopActivitiesLocked(stack, null, null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } @@ -2844,7 +2877,7 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityRecord r = task.topRunningActivityLocked(); if (r != null) { setFocusedActivityLocked(r, "setFocusedTask"); - mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } @@ -2856,7 +2889,8 @@ public final class ActivityManagerService extends ActivityManagerNative /** Sets the task stack listener that gets callbacks when a task stack changes. */ @Override public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException { - synchronized (ActivityManagerService.this) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "registerTaskStackListener()"); + synchronized (this) { if (listener != null) { mTaskStackListeners.register(listener); } @@ -3590,7 +3624,7 @@ public final class ActivityManagerService extends ActivityManagerNative Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); - intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED); + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } @@ -4342,7 +4376,7 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public final int startActivityFromRecents(int taskId, int launchStackId, Bundle bOptions) { + public final int startActivityFromRecents(int taskId, Bundle bOptions) { if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: startActivityFromRecents called without " + START_TASKS_FROM_RECENTS; @@ -4351,19 +4385,24 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { - return startActivityFromRecentsInner(taskId, launchStackId, bOptions); + return startActivityFromRecentsInner(taskId, bOptions); } finally { Binder.restoreCallingIdentity(origId); } } - final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle bOptions) { + final int startActivityFromRecentsInner(int taskId, Bundle bOptions) { final TaskRecord task; final int callingUid; final String callingPackage; final Intent intent; final int userId; synchronized (this) { + final ActivityOptions activityOptions = (bOptions != null) + ? new ActivityOptions(bOptions) : null; + final int launchStackId = (activityOptions != null) + ? activityOptions.getLaunchStackId() : INVALID_STACK_ID; + if (launchStackId == HOME_STACK_ID) { throw new IllegalArgumentException("startActivityFromRecentsInner: Task " + taskId + " can't be launch in the home stack."); @@ -4376,10 +4415,9 @@ public final class ActivityManagerService extends ActivityManagerNative } if (launchStackId != INVALID_STACK_ID) { - if (launchStackId == DOCKED_STACK_ID && bOptions != null) { - ActivityOptions activityOptions = new ActivityOptions(bOptions); - mWindowManager.setDockedStackCreateState(activityOptions.getDockCreateMode(), - null /* initialBounds */); + if (launchStackId == DOCKED_STACK_ID && activityOptions != null) { + mWindowManager.setDockedStackCreateState( + activityOptions.getDockCreateMode(), null /* initialBounds */); } if (task.stack.mStackId != launchStackId) { mStackSupervisor.moveTaskToStackLocked( @@ -4473,7 +4511,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (config != null) { r.frozenBeforeDestroy = true; if (!updateConfigurationLocked(config, r, false)) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } Binder.restoreCallingIdentity(origId); @@ -4811,12 +4849,11 @@ public final class ActivityManagerService extends ActivityManagerNative finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info); } - if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) { - // 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 (!restarting && hasVisibleActivities + && !mStackSupervisor.resumeFocusedStackTopActivityLocked()) { + // 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. mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } } @@ -5873,7 +5910,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Clean-up disabled activities. if (mStackSupervisor.finishDisabledPackageActivitiesLocked( packageName, disabledClasses, true, false, userId) && mBooted) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); mStackSupervisor.scheduleIdleLocked(); } @@ -6061,7 +6098,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (mBooted) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); mStackSupervisor.scheduleIdleLocked(); } } @@ -6736,6 +6773,15 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public final void activityRelaunched(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + synchronized (this) { + mStackSupervisor.activityRelaunchedLocked(token); + } + Binder.restoreCallingIdentity(origId); + } + + @Override public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " " @@ -7257,7 +7303,7 @@ public final class ActivityManagerService extends ActivityManagerNative + "Can't find activity for token=" + token); } - if (!r.info.supportsPip) { + if (!r.supportsPictureInPicture()) { throw new IllegalArgumentException("enterPictureInPictureMode: " + "Picture-In-Picture not supported for r=" + r); } @@ -8994,15 +9040,14 @@ public final class ActivityManagerService extends ActivityManagerNative task.mResizeable = resizeable; mWindowManager.setTaskResizeable(taskId, resizeable); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } @Override public void resizeTask(int taskId, Rect bounds, int resizeMode) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "resizeTask()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9052,8 +9097,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public Rect getTaskBounds(int taskId) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "getTaskBounds()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()"); long ident = Binder.clearCallingIdentity(); Rect rect = new Rect(); try { @@ -9355,8 +9399,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public IActivityContainer createVirtualActivityContainer(IBinder parentActivityToken, IActivityContainerCallback callback) throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "createActivityContainer()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createActivityContainer()"); synchronized (this) { if (parentActivityToken == null) { throw new IllegalArgumentException("parent token must not be null"); @@ -9374,8 +9417,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void deleteActivityContainer(IActivityContainer container) throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "deleteActivityContainer()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "deleteActivityContainer()"); synchronized (this) { mStackSupervisor.deleteActivityContainer(container); } @@ -9383,8 +9425,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "createStackOnDisplay()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()"); synchronized (this) { final int stackId = mStackSupervisor.getNextStackId(); final ActivityStack stack = @@ -9444,8 +9485,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "moveTaskToStack()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); if (stackId == HOME_STACK_ID) { throw new IllegalArgumentException( "moveTaskToStack: Attempt to move task " + taskId + " to home stack"); @@ -9480,8 +9520,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate, Rect initialBounds) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "moveTaskToDockedStack()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()"); synchronized (this) { long ident = Binder.clearCallingIdentity(); try { @@ -9507,8 +9546,7 @@ public final class ActivityManagerService extends ActivityManagerNative */ @Override public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "moveTopActivityToPinnedStack()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTopActivityToPinnedStack()"); synchronized (this) { if (!mSupportsPictureInPicture) { throw new IllegalStateException("moveTopActivityToPinnedStack:" @@ -9526,13 +9564,13 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "resizeStack()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { mStackSupervisor.resizeStackLocked( - stackId, bounds, !PRESERVE_WINDOWS, allowResizeInDockedMode); + stackId, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, + !PRESERVE_WINDOWS, allowResizeInDockedMode); } } finally { Binder.restoreCallingIdentity(ident); @@ -9540,9 +9578,26 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void positionTaskInStack(int taskId, int stackId, int position) { + public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "positionTaskInStack()"); + "resizeDockedStack()"); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, + tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds, + PRESERVE_WINDOWS); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void positionTaskInStack(int taskId, int stackId, int position) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()"); if (stackId == HOME_STACK_ID) { throw new IllegalArgumentException( "positionTaskInStack: Attempt to change the position of task " @@ -9563,8 +9618,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public List<StackInfo> getAllStackInfos() { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "getAllStackInfos()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9577,8 +9631,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public StackInfo getStackInfo(int stackId) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "getStackInfo()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9591,8 +9644,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public boolean isInHomeStack(int taskId) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "getStackInfo()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9706,8 +9758,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void startLockTaskModeOnCurrent() throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "startLockTaskModeOnCurrent"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startLockTaskModeOnCurrent"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -9759,8 +9810,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void stopLockTaskModeOnCurrent() throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "stopLockTaskModeOnCurrent"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopLockTaskModeOnCurrent"); long ident = Binder.clearCallingIdentity(); try { stopLockTaskMode(); @@ -9802,7 +9852,7 @@ public final class ActivityManagerService extends ActivityManagerNative ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager() .queryContentProviders(app.processName, app.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS - | PackageManager.MATCH_ENCRYPTION_DEFAULT); + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); providers = slice != null ? slice.getList() : null; } catch (RemoteException ex) { } @@ -11033,6 +11083,12 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.sendMessageDelayed(nmsg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY); } + /** Notifies all listeners when an Activity is pinned. */ + void notifyActivityPinnedLocked() { + mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); + mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget(); + } + @Override public void notifyCleartextNetwork(int uid, byte[] firstPacket) { mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget(); @@ -12149,7 +12205,9 @@ public final class ActivityManagerService extends ActivityManagerNative private void retrieveSettings() { final ContentResolver resolver = mContext.getContentResolver(); final boolean freeformWindowManagement = - mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT); + mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) + || Settings.Global.getInt( + resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; final boolean supportsPictureInPicture = mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); @@ -12158,9 +12216,8 @@ public final class ActivityManagerService extends ActivityManagerNative final boolean alwaysFinishActivities = Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0; final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0; - final int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0; final boolean forceResizable = Settings.Global.getInt( - resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, defaultForceResizable) != 0; + resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; // Transfer any global setting for forcing RTL layout, into a System Property SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0"); @@ -12603,7 +12660,7 @@ public final class ActivityManagerService extends ActivityManagerNative } finally { Binder.restoreCallingIdentity(ident); } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId); } } @@ -12714,10 +12771,10 @@ public final class ActivityManagerService extends ActivityManagerNative // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. removeProcessLocked(app, false, false, "crash"); - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); return false; } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { mStackSupervisor.finishTopRunningActivityLocked(app, reason); } @@ -17010,7 +17067,7 @@ public final class ActivityManagerService extends ActivityManagerNative private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, int callingUid, int[] users) { // TODO: come back and remove this assumption to triage all broadcasts - int pmFlags = STOCK_PM_FLAGS | PackageManager.MATCH_ENCRYPTION_DEFAULT; + int pmFlags = STOCK_PM_FLAGS | PackageManager.MATCH_DEBUG_TRIAGED_MISSING; List<ResolveInfo> receivers = null; try { @@ -17933,8 +17990,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void suppressResizeConfigChanges(boolean suppress) throws RemoteException { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "suppressResizeConfigChanges()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()"); synchronized (this) { mSuppressResizeConfigChanges = suppress; } @@ -17942,8 +17998,7 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void moveTasksToFullscreenStack(int fromStackId) { - enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, - "moveTasksToFullscreenStack()"); + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()"); if (fromStackId == HOME_STACK_ID) { throw new IllegalArgumentException("You can't move tasks from the home stack."); } @@ -18062,10 +18117,18 @@ public final class ActivityManagerService extends ActivityManagerNative EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { - if (mSupportedSystemLocales == null) { - mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales(); + final Locale locale; + if (values.getLocales().size() == 1) { + // This is an optimization to avoid the JNI call when the result of + // getFirstMatch() does not depend on the supported locales. + locale = values.getLocales().getPrimary(); + } else { + if (mSupportedSystemLocales == null) { + mSupportedSystemLocales = + Resources.getSystem().getAssets().getLocales(); + } + locale = values.getLocales().getFirstMatch(mSupportedSystemLocales); } - final Locale locale = values.getLocales().getFirstMatch(mSupportedSystemLocales); SystemProperties.set("persist.sys.locale", locale.toLanguageTag()); mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, locale)); @@ -20889,7 +20952,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void moveToFront() { checkCaller(); // Will bring task to front if it already has a root activity. - startActivityFromRecentsInner(mTaskId, INVALID_STACK_ID, null); + startActivityFromRecentsInner(mTaskId, null); } @Override diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 9680382e922d..4101dde37198 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -26,7 +26,7 @@ class ActivityMetricsLogger { // Preallocated strings we are sending to tron, so we don't have to allocate a new one every // time we log. private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = { - "tron_varz_window_time_0", "tron_varz_window_time_1", "tron_varz_window_time_2"}; + "window_time_0", "window_time_1", "window_time_2"}; private int mWindowState = WINDOW_STATE_STANDARD; private long mLastLogTimeSecs; diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 4d9120b09478..4fb87c3a2660 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -16,7 +16,11 @@ package com.android.server.am; +import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE; +import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; @@ -739,6 +743,22 @@ final class ActivityRecord { (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0); } + boolean isFocusable() { + return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable(); + } + + boolean isResizeable() { + return (info.flags & FLAG_RESIZEABLE) != 0; + } + + boolean supportsPictureInPicture() { + return (info.flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0; + } + + boolean isAlwaysFocusable() { + return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0; + } + void makeFinishingLocked() { if (!finishing) { if (task != null && task.stack != null diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 1492e23e37e4..8d9cb5897bd6 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -560,7 +560,7 @@ final class ActivityStack { // TODO(multi-display): Needs to also work if focus is moving to the non-home display. if (isOnHomeDisplay()) { - mStackSupervisor.setFocusStack(reason, this); + mStackSupervisor.setFocusStackUnchecked(reason, this); } if (task != null) { insertTaskAtTop(task, null); @@ -572,6 +572,16 @@ final class ActivityStack { } } + boolean isFocusable() { + if (StackId.canReceiveKeys(mStackId)) { + return true; + } + // The stack isn't focusable. See if its top activity is focusable to force focus on the + // stack. + final ActivityRecord r = topRunningActivityLocked(); + return r != null && r.isFocusable(); + } + final boolean isAttached() { return mStacks != null; } @@ -917,7 +927,7 @@ final class ActivityStack { if (prev == null) { if (!resuming) { Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; } @@ -1008,7 +1018,7 @@ final class ActivityStack { // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next."); if (!resuming) { - mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; } @@ -1075,7 +1085,7 @@ final class ActivityStack { } else { if (r.configDestroy) { destroyActivityLocked(r, true, "stop-config"); - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { mStackSupervisor.updatePreviousProcessLocked(r); } @@ -1131,17 +1141,16 @@ final class ActivityStack { if (resumeNext) { final ActivityStack topStack = mStackSupervisor.getFocusedStack(); if (!mService.isSleepingOrShuttingDown()) { - mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null); } else { mStackSupervisor.checkReadyForSleepLocked(); ActivityRecord top = topStack.topRunningActivityLocked(); if (top == null || (prev != null && top != prev)) { - // If there are no more activities available to run, - // do resume anyway to start something. Also if the top - // activity on the stack is not the just paused activity, - // we need to go ahead and resume it to ensure we complete - // an in-flight app switch. - mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null); + // If there are no more activities available to run, do resume anyway to start + // something. Also if the top activity on the stack is not the just paused + // activity, we need to go ahead and resume it to ensure we complete an + // in-flight app switch. + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } @@ -1296,7 +1305,7 @@ final class ActivityStack { return null; } - private ActivityStack getNextVisibleStackLocked() { + ActivityStack getNextFocusableStackLocked() { ArrayList<ActivityStack> stacks = mStacks; final ActivityRecord parent = mActivityContainer.mParentActivity; if (parent != null) { @@ -1305,7 +1314,7 @@ final class ActivityStack { if (stacks != null) { for (int i = stacks.size() - 1; i >= 0; --i) { ActivityStack stack = stacks.get(i); - if (stack != this && stack.isStackVisibleLocked()) { + if (stack != this && stack.isFocusable() && stack.isStackVisibleLocked()) { return stack; } } @@ -1478,7 +1487,7 @@ final class ActivityStack { boolean aboveTop = top != null; final boolean stackInvisible = !isStackVisibleLocked(); boolean behindFullscreenActivity = stackInvisible; - boolean noStackActivityResumed = (isInStackLocked(starting) == null); + boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); @@ -1509,18 +1518,18 @@ final class ActivityStack { if (r.app == null || r.app.thread == null) { if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop, - noStackActivityResumed, r)) { + resumeNextActivity, r)) { if (activityNdx >= activities.size()) { // Record may be removed if its process needs to restart. activityNdx = activities.size() - 1; } else { - noStackActivityResumed = false; + resumeNextActivity = false; } } } else if (r.visible) { // If this activity is already visible, then there is nothing to do here. if (handleAlreadyVisible(r)) { - noStackActivityResumed = false; + resumeNextActivity = false; } } else { makeVisible(starting, r); @@ -1561,7 +1570,7 @@ final class ActivityStack { } private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges, - boolean isTop, boolean noStackActivityResumed, ActivityRecord r) { + boolean isTop, boolean andResume, ActivityRecord r) { // We need to make sure the app is running if it's the top, or it is just made visible from // invisible. If the app is already visible, it must have died while it was visible. In this // case, we'll show the dead window but will not restart the app. Otherwise we could end up @@ -1578,7 +1587,7 @@ final class ActivityStack { setVisible(r, true); } if (r != starting) { - mStackSupervisor.startSpecificActivityLocked(r, noStackActivityResumed, false); + mStackSupervisor.startSpecificActivityLocked(r, andResume, false); return true; } } @@ -1772,15 +1781,17 @@ final class ActivityStack { * * @param prev The previously resumed activity, for when in the process * of pausing; can be null to call from elsewhere. + * @param options Activity options. * * @return Returns true if something is being resumed, or false if * nothing happened. + * + * NOTE: It is not safe to call this method directly as it can cause an activity in a + * non-focused stack to be resumed. + * Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the + * right activity for the current system state. */ - final boolean resumeTopActivityLocked(ActivityRecord prev) { - return resumeTopActivityLocked(prev, null); - } - - final boolean resumeTopActivityLocked(ActivityRecord prev, ActivityOptions options) { + boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { if (mStackSupervisor.inResumeTopActivity) { // Don't even start recursing. return false; @@ -1831,14 +1842,13 @@ final class ActivityStack { if (next == null) { // There are no more activities! final String reason = "noMoreActivities"; - if (!mFullscreen) { + if (!mFullscreen && adjustFocusToNextFocusableStackLocked(reason)) { // Try to move focus to the next visible stack with a running activity if this // stack is not covering the entire screen. - final ActivityStack stack = getNextVisibleStackLocked(); - if (adjustFocusToNextVisibleStackLocked(stack, reason)) { - return mStackSupervisor.resumeTopActivitiesLocked(stack, prev, null); - } + return mStackSupervisor.resumeFocusedStackTopActivityLocked( + mStackSupervisor.getFocusedStack(), prev, null); } + // Let's just start up the Launcher... ActivityOptions.abort(options); if (DEBUG_STATES) Slog.d(TAG_STATES, @@ -2870,7 +2880,7 @@ final class ActivityStack { final ActivityRecord next = topRunningActivityLocked(); final String myReason = reason + " adjustFocus"; if (next != r) { - if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) { + if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) { // For freeform, docked, and pinned stacks we always keep the focus within the // stack as long as there is a running activity in the stack that we can adjust // focus to. @@ -2882,8 +2892,7 @@ final class ActivityStack { // For non-fullscreen stack, we want to move the focus to the next visible // stack to prevent the home screen from moving to the top and obscuring // other visible stacks. - if (!mFullscreen - && adjustFocusToNextVisibleStackLocked(null, myReason)) { + if (!mFullscreen && adjustFocusToNextFocusableStackLocked(myReason)) { return; } // Move the home stack to the top if this stack is fullscreen or there is no @@ -2897,24 +2906,16 @@ final class ActivityStack { } } - final ActivityRecord top = mStackSupervisor.topRunningActivityLocked(); - if (top != null) { - mService.setFocusedActivityLocked(top, myReason); - } + mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason); } - private boolean adjustFocusToNextVisibleStackLocked(ActivityStack inStack, String reason) { - final ActivityStack stack = (inStack != null) ? inStack : getNextVisibleStackLocked(); - final String myReason = reason + " adjustFocusToNextVisibleStack"; + private boolean adjustFocusToNextFocusableStackLocked(String reason) { + final ActivityStack stack = getNextFocusableStackLocked(); + final String myReason = reason + " adjustFocusToNextFocusableStack"; if (stack == null) { return false; } - final ActivityRecord top = stack.topRunningActivityLocked(); - if (top == null) { - return false; - } - mService.setFocusedActivityLocked(top, myReason); - return true; + return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason); } final void stopActivityLocked(ActivityRecord r) { @@ -3224,7 +3225,7 @@ final class ActivityStack { r.makeFinishingLocked(); boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm"); if (activityRemoved) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r=" + r + @@ -3237,7 +3238,7 @@ final class ActivityStack { if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r); mStackSupervisor.mFinishingActivities.add(r); r.resumeKeyDispatchingLocked(); - mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); return r; } @@ -3395,7 +3396,7 @@ final class ActivityStack { if (mPausingActivity == r) { mPausingActivity = null; } - mService.clearFocusedActivity(r); + mService.resetFocusedActivityIfNeededLocked(r); r.configDestroy = false; r.frozenBeforeDestroy = false; @@ -3526,7 +3527,7 @@ final class ActivityStack { } } if (activityRemoved) { - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } @@ -3700,7 +3701,7 @@ final class ActivityStack { removeActivityFromHistoryLocked(r, reason); } } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } finally { Binder.restoreCallingIdentity(origId); } @@ -3738,7 +3739,7 @@ final class ActivityStack { setVisibleBehindActivity(null); mStackSupervisor.scheduleIdleTimeoutLocked(null); } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); } boolean hasVisibleBehindActivity() { @@ -3953,7 +3954,7 @@ final class ActivityStack { updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options); } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); if (VALIDATE_TOKENS) { @@ -4016,7 +4017,7 @@ final class ActivityStack { if (fullscreenStack != null && fullscreenStack.hasVisibleBehindActivity()) { final ActivityRecord visibleBehind = fullscreenStack.getVisibleBehindActivity(); mService.setFocusedActivityLocked(visibleBehind, "moveTaskToBack"); - mStackSupervisor.resumeTopActivitiesLocked(fullscreenStack, null, null); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); return true; } } @@ -4071,7 +4072,7 @@ final class ActivityStack { return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack"); } - mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); return true; } @@ -4313,6 +4314,7 @@ final class ActivityStack { r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes, !andResume, new Configuration(mService.mConfiguration), new Configuration(r.task.mOverrideConfig), preserveWindow); + mStackSupervisor.activityRelaunchingLocked(r); // Note: don't need to call pauseIfSleepingLocked() here, because // the caller will only pass in 'andResume' if this activity is // currently resumed, which implies we aren't sleeping. @@ -4661,7 +4663,7 @@ final class ActivityStack { // We only need to adjust focused stack if this stack is in focus. if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) { String myReason = reason + " leftTaskHistoryEmpty"; - if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) { + if (mFullscreen || !adjustFocusToNextFocusableStackLocked(myReason)) { mStackSupervisor.moveHomeStackToFront(myReason); } } @@ -4734,10 +4736,7 @@ final class ActivityStack { private void postAddTask(TaskRecord task, ActivityStack prevStack) { if (prevStack != null) { - if (prevStack != this - && (prevStack.mStackId == PINNED_STACK_ID || mStackId == PINNED_STACK_ID)) { - task.reportPictureInPictureModeChange(); - } + task.reportPictureInPictureModeChangeIfNeeded(prevStack); } else if (task.voiceSession != null) { try { task.voiceSession.taskStarted(task.intent, task.taskId); @@ -4751,32 +4750,28 @@ final class ActivityStack { task.updateOverrideConfiguration(bounds); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, - (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, - r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind, - bounds, task.mOverrideConfig, !r.isHomeActivity()); + (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges, + task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig, + !r.isHomeActivity(), r.isAlwaysFocusable()); mWindowManager.setTaskResizeable(task.taskId, task.mResizeable); r.taskConfigOverride = task.mOverrideConfig; } - void setFocusAndResumeStateIfNeeded( - ActivityRecord r, boolean setFocus, boolean setResume, String reason) { - // If the activity had focus before move focus to this stack. - if (setFocus) { - // If the activity owns the last resumed activity, transfer that together, - // so that we don't resume the same activity again in the new stack. - // Apps may depend on onResume()/onPause() being called in pairs. - if (setResume) { - mResumedActivity = r; - // Move the stack in which we are placing the activity to the front. We don't use - // ActivityManagerService.setFocusedActivityLocked, because if the activity is - // already focused, the call will short-circuit and do nothing. - moveToFront(reason); - } else { - // We need to not only move the stack to the front, but also have the activity - // focused. This will achieve both goals. - mService.setFocusedActivityLocked(r, reason); - } + void moveToFrontAndResumeStateIfNeeded( + ActivityRecord r, boolean moveToFront, boolean setResume, String reason) { + if (!moveToFront) { + return; } + + // If the activity owns the last resumed activity, transfer that together, + // so that we don't resume the same activity again in the new stack. + // Apps may depend on onResume()/onPause() being called in pairs. + if (setResume) { + mResumedActivity = r; + } + // Move the stack in which we are placing the activity to the front. The call will also + // make sure the activity focus is set. + moveToFront(reason); } /** @@ -4800,8 +4795,11 @@ final class ActivityStack { r.setTask(task, null); task.addActivityToTop(r); setAppTask(r, task); - task.reportPictureInPictureModeChange(); - setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack"); + task.reportPictureInPictureModeChangeIfNeeded(prevStack); + moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack"); + if (wasResumed) { + prevStack.mResumedActivity = null; + } } private void setAppTask(ActivityRecord r, TaskRecord task) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 32671acfcc30..80a75ce74312 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -16,70 +16,6 @@ package com.android.server.am; -import static android.Manifest.permission.START_ANY_ACTIVITY; -import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; -import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; -import static android.app.ActivityManager.RESIZE_MODE_FORCED; -import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; -import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; -import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG; -import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; -import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; -import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE; -import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; -import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; -import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; -import static com.android.server.am.ActivityStack.ActivityState.PAUSED; -import static com.android.server.am.ActivityStack.ActivityState.PAUSING; -import static com.android.server.am.ActivityStack.ActivityState.RESUMED; -import static com.android.server.am.ActivityStack.ActivityState.STOPPED; -import static com.android.server.am.ActivityStack.ActivityState.STOPPING; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; - import android.Manifest; import android.app.Activity; import android.app.ActivityManager; @@ -164,6 +100,70 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import static android.Manifest.permission.START_ANY_ACTIVITY; +import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; +import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; +import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; +import static android.app.ActivityManager.RESIZE_MODE_FORCED; +import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; +import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.HOME_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG; +import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; +import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; +import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; +import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; +import static com.android.server.am.ActivityStack.ActivityState.PAUSED; +import static com.android.server.am.ActivityStack.ActivityState.PAUSING; +import static com.android.server.am.ActivityStack.ActivityState.RESUMED; +import static com.android.server.am.ActivityStack.ActivityState.STOPPED; +import static com.android.server.am.ActivityStack.ActivityState.STOPPING; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; + public final class ActivityStackSupervisor implements DisplayListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM; private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS; @@ -370,7 +370,7 @@ public final class ActivityStackSupervisor implements DisplayListener { */ private LockTaskNotify mLockTaskNotify; - /** Used to keep resumeTopActivityLocked() from being entered recursively */ + /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */ boolean inResumeTopActivity; // temp. rects used during resize calculation so we don't need to create a new object each time. @@ -379,6 +379,7 @@ public final class ActivityStackSupervisor implements DisplayListener { private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>(); private final SparseArray<Rect> mTmpBounds = new SparseArray<>(); + private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>(); // The default minimal size that will be used if the activity doesn't specify its minimal size. // It will be calculated when the default display gets added. @@ -389,6 +390,8 @@ public final class ActivityStackSupervisor implements DisplayListener { private final ActivityMetricsLogger mActivityMetricsLogger; + private final ResizeDockedStackTimeout mResizeDockedStackTimeout; + static class FindTaskResult { ActivityRecord r; boolean matchedByRootAffinity; @@ -432,6 +435,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mRecentTasks = recentTasks; mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); + mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler); } /** @@ -492,8 +496,8 @@ public final class ActivityStackSupervisor implements DisplayListener { calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay); } - createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY, true); - mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID); + mHomeStack = mFocusedStack = mLastFocusedStack = + getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); } @@ -537,16 +541,30 @@ public final class ActivityStackSupervisor implements DisplayListener { return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1)); } - void setFocusStack(String reason, ActivityStack focusedStack) { - mLastFocusedStack = mFocusedStack; - mFocusedStack = focusedStack; + /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */ + void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) { + if (!focusCandidate.isFocusable()) { + // The focus candidate isn't focusable. Move focus to the top stack that is focusable. + focusCandidate = focusCandidate.getNextFocusableStackLocked(); + } - EventLogTags.writeAmFocusedStack( - mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(), - mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason); + if (focusCandidate != mFocusedStack) { + mLastFocusedStack = mFocusedStack; + mFocusedStack = focusCandidate; + + EventLogTags.writeAmFocusedStack( + mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(), + mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason); + } + + final ActivityRecord r = topRunningActivityLocked(); + if (mService.mFocusedActivity != r) { + // The focus activity should always be the top activity in the focused stack. + // There will be chaos and anarchy if it isn't... + mService.setFocusedActivityLocked(r, reason + " setFocusStack"); + } if (mService.mBooting || !mService.mBooted) { - final ActivityRecord r = topRunningActivityLocked(); if (r != null && r.idle) { checkFinishBootingLocked(); } @@ -591,12 +609,14 @@ public final class ActivityStackSupervisor implements DisplayListener { mHomeStack.moveHomeStackTaskToTop(homeStackTaskType); ActivityRecord r = getHomeActivity(); + final String myReason = reason + " resumeHomeStackTask"; + // Only resume home activity if isn't finishing. if (r != null && !r.finishing) { - mService.setFocusedActivityLocked(r, reason); - return resumeTopActivitiesLocked(mHomeStack, prev, null); + mService.setFocusedActivityLocked(r, myReason); + return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null); } - return mService.startHomeActivityLocked(mCurrentUser, reason); + return mService.startHomeActivityLocked(mCurrentUser, myReason); } TaskRecord anyTaskForIdLocked(int id) { @@ -893,7 +913,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityStack> stacks = mHomeStack.mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - if (stack != focusedStack && isFrontStack(stack)) { + if (stack != focusedStack && isFrontStack(stack) && stack.isFocusable()) { r = stack.topRunningActivityLocked(); if (r != null) { return r; @@ -1148,14 +1168,12 @@ public final class ActivityStackSupervisor implements DisplayListener { // a resume. stack.minimalResumeActivityLocked(r); } else { - // This activity is not starting in the resumed state... which - // should look like we asked it to pause+stop (but remain visible), - // and it has done so and reported back the current icicle and - // other state. + // This activity is not starting in the resumed state... which should look like we asked + // it to pause+stop (but remain visible), and it has done so and reported back the + // current icicle and other state. if (DEBUG_STATES) Slog.v(TAG_STATES, - "Moving to STOPPED: " + r + " (starting in stopped state)"); - r.state = STOPPED; - r.stopped = true; + "Moving to PAUSED: " + r + " (starting in paused state)"); + r.state = PAUSED; } // Launch the new version setup screen if needed. We do this -after- @@ -1345,30 +1363,20 @@ public final class ActivityStackSupervisor implements DisplayListener { return ACTIVITY_RESTRICTION_NONE; } - boolean setFocusedStack(ActivityRecord r, String reason) { + boolean moveActivityStackToFront(ActivityRecord r, String reason) { if (r == null) { // Not sure what you are trying to do, but it is not going to work... return false; } final TaskRecord task = r.task; if (task == null || task.stack == null) { - Slog.w(TAG, "Can't set focus stack for r=" + r + " task=" + task); + Slog.w(TAG, "Can't move stack to front for r=" + r + " task=" + task); return false; } task.stack.moveToFront(reason, task); return true; } - Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) { - Rect newBounds = null; - if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) { - if (canUseActivityOptionsLaunchBounds(options)) { - newBounds = options.getLaunchBounds(); - } - } - return newBounds; - } - void setLaunchSource(int uid) { mLaunchingActivity.setWorkSource(new WorkSource(uid)); } @@ -1518,7 +1526,7 @@ public final class ActivityStackSupervisor implements DisplayListener { //mWindowManager.dump(); if (activityRemoved) { - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } return r; @@ -1611,35 +1619,17 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - boolean resumeTopActivitiesLocked() { - return resumeTopActivitiesLocked(null, null, null); + boolean resumeFocusedStackTopActivityLocked() { + return resumeFocusedStackTopActivityLocked(null, null, null); } - boolean resumeTopActivitiesLocked( + boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { - if (targetStack == null) { - targetStack = mFocusedStack; - } - // Do targetStack first. - boolean result = false; - if (isFocusedStack(targetStack)) { - result = targetStack.resumeTopActivityLocked(target, targetOptions); - } - - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; - for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = stacks.get(stackNdx); - if (stack == targetStack) { - // Already started above. - continue; - } - if (isFocusedStack(stack)) { - stack.resumeTopActivityLocked(null); - } - } + if (targetStack != null && isFocusedStack(targetStack)) { + return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } - return result; + mFocusedStack.resumeTopActivityUncheckedLocked(null, null); + return false; } void finishTopRunningActivityLocked(ProcessRecord app, String reason) { @@ -1681,10 +1671,13 @@ public final class ActivityStackSupervisor implements DisplayListener { } if (task.mResizeable && options != null) { - if (canUseActivityOptionsLaunchBounds(options)) { + int stackId = options.getLaunchStackId(); + if (canUseActivityOptionsLaunchBounds(options, stackId)) { Rect bounds = options.getLaunchBounds(); task.updateOverrideConfiguration(bounds); - final int stackId = task.getLaunchStackId(); + if (stackId == INVALID_STACK_ID) { + stackId = task.getLaunchStackId(); + } if (stackId != task.stack.mStackId) { moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason); // moveTaskToStackUncheckedLocked() should already placed the task on top, @@ -1706,10 +1699,14 @@ public final class ActivityStackSupervisor implements DisplayListener { "findTaskToMoveToFront: moved to front of stack=" + task.stack); } - private boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) { + boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) { // We use the launch bounds in the activity options is the device supports freeform - // window management. - return options.hasLaunchBounds() && mService.mSupportsFreeformWindowManagement; + // window management or is launching into the pinned stack. + if (!options.hasLaunchBounds()) { + return false; + } + return (mService.mSupportsPictureInPicture && launchStackId == PINNED_STACK_ID) + || mService.mSupportsFreeformWindowManagement; } ActivityStack getStack(int stackId) { @@ -1806,16 +1803,20 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - void resizeStackLocked(int stackId, Rect bounds, boolean preserveWindows, - boolean allowResizeInDockedMode) { + void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds, + boolean preserveWindows, boolean allowResizeInDockedMode) { + if (stackId == DOCKED_STACK_ID) { + resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null, + preserveWindows); + return; + } final ActivityStack stack = getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; } - if (!allowResizeInDockedMode - && stackId != DOCKED_STACK_ID && getStack(DOCKED_STACK_ID) != null) { + if (!allowResizeInDockedMode && getStack(DOCKED_STACK_ID) != null) { // If the docked stack exist we don't allow resizes of stacks not caused by the docked // stack size changing so things don't get out of sync. return; @@ -1824,94 +1825,137 @@ public final class ActivityStackSupervisor implements DisplayListener { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId); mWindowManager.deferSurfaceLayout(); try { + resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds); + ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows); + } finally { + mWindowManager.continueSurfaceLayout(); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + } - if (bounds != null && mWindowManager.isFullscreenBounds(stackId, bounds)) { - // The bounds passed in corresponds to the fullscreen bounds which we normally - // represent with null. Go ahead and set it to null so that all tasks configuration - // can have the right fullscreen state. - bounds = null; - } - - ActivityRecord r = stack.topRunningActivityLocked(); + private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds, + Rect tempTaskInsetBounds) { + if (bounds != null && mWindowManager.isFullscreenBounds(stack.mStackId, bounds)) { + // The bounds passed in corresponds to the fullscreen bounds which we normally + // represent with null. Go ahead and set it to null so that all tasks configuration + // can have the right fullscreen state. + bounds = null; + } - mTmpBounds.clear(); - mTmpConfigs.clear(); - ArrayList<TaskRecord> tasks = stack.getAllTasks(); - for (int i = tasks.size() - 1; i >= 0; i--) { - TaskRecord task = tasks.get(i); - if (task.mResizeable) { - if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { - // For freeform stack we don't adjust the size of the tasks to match that - // of the stack, but we do try to make sure the tasks are still contained - // with the bounds of the stack. - tempRect2.set(task.mBounds); - fitWithinBounds(tempRect2, bounds); - task.updateOverrideConfiguration(tempRect2); - } else { - task.updateOverrideConfiguration(bounds); - } + mTmpBounds.clear(); + mTmpConfigs.clear(); + mTmpInsetBounds.clear(); + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + TaskRecord task = tasks.get(i); + if (task.mResizeable) { + if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) { + // For freeform stack we don't adjust the size of the tasks to match that + // of the stack, but we do try to make sure the tasks are still contained + // with the bounds of the stack. + tempRect2.set(task.mBounds); + fitWithinBounds(tempRect2, bounds); + task.updateOverrideConfiguration(tempRect2); + } else { + task.updateOverrideConfiguration(tempTaskBounds != null + ? tempTaskBounds : bounds); } + } - mTmpConfigs.put(task.taskId, task.mOverrideConfig); - mTmpBounds.put(task.taskId, task.mBounds); + mTmpConfigs.put(task.taskId, task.mOverrideConfig); + mTmpBounds.put(task.taskId, task.mBounds); + if (tempTaskInsetBounds != null) { + mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds); } - stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds); - if (stack.mStackId == DOCKED_STACK_ID) { - // Dock stack funness...Yay! - if (stack.mFullscreen) { - // The dock stack went fullscreen which is kinda like dismissing it. - // In this case we make all other static stacks fullscreen and move all - // docked stack tasks to the fullscreen stack. - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) { - resizeStackLocked(i, null, preserveWindows, true); - } - } + } - final int count = tasks.size(); - for (int i = 0; i < count; i++) { - moveTaskToStackLocked(tasks.get(i).taskId, - FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack", - false /* animate */); - } + // We might trigger a configuration change. Save the current task bounds for freezing. + mWindowManager.prepareFreezingTaskBounds(stack.mStackId); + stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs, + mTmpBounds, mTmpInsetBounds); + stack.setBounds(bounds); + } - // stack shouldn't contain anymore activities, so nothing to resume. - r = null; - } else { - // Docked stacks occupy a dedicated region on screen so the size of all other - // static stacks need to be adjusted so they don't overlap with the docked stack. - // We get the bounds to use from window manager which has been adjusted for any - // screen controls and is also the same for all stacks. - mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect); - - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - if (StackId.isResizeableByDockedStack(i)) { - ActivityStack otherStack = getStack(i); - if (otherStack != null) { - resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true); - } - } + private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r, + boolean preserveWindows) { + if (r == null) { + return; + } + final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, + preserveWindows); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + ensureActivitiesVisibleLocked(r, 0, preserveWindows); + if (!updated) { + resumeFocusedStackTopActivityLocked(); + } + } + + void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + Rect tempDockedTaskInsetBounds, + Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) { + final ActivityStack stack = getStack(DOCKED_STACK_ID); + if (stack == null) { + Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); + return; + } + + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack"); + mWindowManager.deferSurfaceLayout(); + try { + ActivityRecord r = stack.topRunningActivityLocked(); + resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds, + tempDockedTaskInsetBounds); + + if (stack.mFullscreen) { + // The dock stack went fullscreen which is kinda like dismissing it. + // In this case we make all other static stacks fullscreen and move all + // docked stack tasks to the fullscreen stack. + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) { + resizeStackLocked(i, null, null, null, preserveWindows, + true /* allowResizeInDockedMode */); } } - // Since we are resizing the stack, all other operations should strive to preserve - // windows. - preserveWindows = true; - } - stack.setBounds(bounds); - if (r != null) { - final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows); - // And we need to make sure at this point that all other activities - // are made visible with the correct configuration. - ensureActivitiesVisibleLocked(r, 0, preserveWindows); - if (!updated) { - resumeTopActivitiesLocked(stack, null, null); + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + final int count = tasks.size(); + for (int i = 0; i < count; i++) { + moveTaskToStackLocked(tasks.get(i).taskId, + FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack", + false /* animate */); + } + + // stack shouldn't contain anymore activities, so nothing to resume. + r = null; + } else { + // Docked stacks occupy a dedicated region on screen so the size of all other + // static stacks need to be adjusted so they don't overlap with the docked stack. + // We get the bounds to use from window manager which has been adjusted for any + // screen controls and is also the same for all stacks. + mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect); + for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { + if (StackId.isResizeableByDockedStack(i)) { + ActivityStack otherStack = getStack(i); + if (otherStack != null) { + resizeStackLocked(i, tempRect, tempOtherTaskBounds, + tempOtherTaskInsetBounds, preserveWindows, + true /* allowResizeInDockedMode */); + } + } } } + ensureConfigurationAndResume(stack, r, preserveWindows); } finally { mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } + + mResizeDockedStackTimeout.notifyResizing(dockedBounds, + tempDockedTaskBounds != null + || tempDockedTaskInsetBounds != null + || tempOtherTaskBounds != null + || tempOtherTaskInsetBounds != null); } void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) { @@ -1961,7 +2005,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // All other activities must be made visible with their correct configuration. ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS); if (!kept) { - resumeTopActivitiesLocked(stack, null, null); + resumeFocusedStackTopActivityLocked(); } } } @@ -2083,8 +2127,14 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityStack moveTaskToStackUncheckedLocked( TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) { final ActivityRecord r = task.getTopActivity(); - final boolean wasFocused = isFocusedStack(task.stack) && (topRunningActivityLocked() == r); - final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r); + final ActivityStack prevStack = task.stack; + final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r); + final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r); + // In some cases the focused stack isn't the front stack. E.g. pinned stack. + // Whenever we are moving the top activity from the front stack we want to make sure to move + // the stack to the front. + final boolean wasFront = isFrontStack(prevStack) + && (prevStack.topRunningActivityLocked() == r); final boolean resizeable = task.mResizeable; // Temporarily disable resizeablility of task we are moving. We don't want it to be resized @@ -2097,9 +2147,9 @@ public final class ActivityStackSupervisor implements DisplayListener { stack.addTask(task, toTop, reason); // If the task had focus before (or we're requested to move focus), - // move focus to the new stack. - stack.setFocusAndResumeStateIfNeeded( - r, forceFocus || wasFocused, wasResumed, reason); + // move focus to the new stack by moving the stack to the front. + stack.moveToFrontAndResumeStateIfNeeded( + r, forceFocus || wasFocused || wasFront, wasResumed, reason); return stack; } @@ -2149,7 +2199,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); if (!task.mResizeable && isStackDockedInEffect(stackId)) { showNonResizeableDockToast(taskId); @@ -2170,7 +2220,7 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - if (!mService.mForceResizableActivities && !r.info.supportsPip) { + if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for " + " r=" + r); @@ -2194,13 +2244,18 @@ public final class ActivityStackSupervisor implements DisplayListener { } if (bounds != null) { - resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS, true); + resizeStackLocked(stackId, bounds, null /* tempTaskBounds */, + null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true); } // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); + + if (stackId == PINNED_STACK_ID) { + mService.notifyActivityPinnedLocked(); + } } void positionTaskInStackLocked(int taskId, int stackId, int position) { @@ -2219,7 +2274,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } ActivityRecord findTaskLocked(ActivityRecord r) { @@ -2329,7 +2384,7 @@ public final class ActivityStackSupervisor implements DisplayListener { final ActivityStack stack = stacks.get(stackNdx); stack.awakeFromSleepingLocked(); if (isFocusedStack(stack)) { - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } } } @@ -3124,7 +3179,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskModeLocked: Tasks remaining, can't unlock"); lockedTask.performClearTaskLocked(); - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); return; } } @@ -3165,7 +3220,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if (andResume) { findTaskToMoveToFrontLocked(task, 0, null, reason); - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } } @@ -3233,7 +3288,7 @@ public final class ActivityStackSupervisor implements DisplayListener { didSomething = true; } if (didSomething) { - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } } @@ -3241,6 +3296,14 @@ public final class ActivityStackSupervisor implements DisplayListener { return mLockTaskModeState; } + void activityRelaunchedLocked(IBinder token) { + mWindowManager.notifyAppRelaunchingFinished(token); + } + + void activityRelaunchingLocked(ActivityRecord r) { + mWindowManager.notifyAppRelaunching(r.appToken); + } + void logStackState() { mActivityMetricsLogger.logWindowState(); } @@ -3280,7 +3343,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (mService) { - resumeTopActivitiesLocked(); + resumeFocusedStackTopActivityLocked(); } } break; case SLEEP_TIMEOUT_MSG: { @@ -3661,7 +3724,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mSurface = surface; if (surface != null) { - mStack.resumeTopActivityLocked(null); + resumeFocusedStackTopActivityLocked(); } else { mContainerState = CONTAINER_STATE_NO_SURFACE; ((VirtualActivityDisplay) mActivityDisplay).setSurface(null); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index b16e160d9505..f2864d4dd470 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1,14 +1,38 @@ package com.android.server.am; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.ActivityManager.START_CLASS_NOT_FOUND; +import static android.app.ActivityManager.START_DELIVERED_TO_TOP; +import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED; +import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER; +import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; +import static android.app.ActivityManager.START_SUCCESS; +import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.app.ActivityManager.StackId; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.HOME_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; +import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP; +import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT; +import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; +import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; +import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; +import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; @@ -79,7 +103,7 @@ import java.util.ArrayList; * This class collects all the logic for determining how an intent and flags should be turned into * an activity and associated task and stack. */ -public class ActivityStarter { +class ActivityStarter { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_AM; private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; @@ -92,6 +116,79 @@ public class ActivityStarter { final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>(); + // Share state variable among methods when starting an activity. + private ActivityRecord mStartActivity; + private Intent mIntent; + private int mCallingUid; + private ActivityOptions mOptions; + + private boolean mLaunchSingleTop; + private boolean mLaunchSingleInstance; + private boolean mLaunchSingleTask; + private boolean mLaunchTaskBehind; + private int mLaunchFlags; + + private Rect mLaunchBounds; + + private ActivityRecord mNotTop; + private boolean mDoResume; + private int mStartFlags; + private ActivityRecord mSourceRecord; + + private TaskRecord mInTask; + private boolean mAddingToTask; + private TaskRecord mReuseTask; + + private ActivityInfo mNewTaskInfo; + private Intent mNewTaskIntent; + private ActivityStack mSourceStack; + private ActivityStack mTargetStack; + // TODO: Is the mMoveHome flag really needed? + private boolean mMovedHome; + private boolean mMovedToFront; + private boolean mNoAnimation; + private boolean mKeepCurTransition; + + private IVoiceInteractionSession mVoiceSession; + private IVoiceInteractor mVoiceInteractor; + + private void reset() { + mStartActivity = null; + mIntent = null; + mCallingUid = -1; + mOptions = null; + + mLaunchSingleTop = false; + mLaunchSingleInstance = false; + mLaunchSingleTask = false; + mLaunchTaskBehind = false; + mLaunchFlags = 0; + + mLaunchBounds = null; + + mNotTop = null; + mDoResume = false; + mStartFlags = 0; + mSourceRecord = null; + + mInTask = null; + mAddingToTask = false; + mReuseTask = null; + + mNewTaskInfo = null; + mNewTaskIntent = null; + mSourceStack = null; + + mTargetStack = null; + mMovedHome = false; + mMovedToFront = false; + mNoAnimation = false; + mKeepCurTransition = false; + + mVoiceSession = null; + mVoiceInteractor = null; + } + ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) { mService = service; mSupervisor = supervisor; @@ -234,11 +331,10 @@ public class ActivityStarter { final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack; - if (err != ActivityManager.START_SUCCESS) { + if (err != START_SUCCESS) { if (resultRecord != null) { - resultStack.sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, - Activity.RESULT_CANCELED, null); + resultStack.sendActivityResultLocked( + -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null); } ActivityOptions.abort(options); return err; @@ -298,12 +394,12 @@ public class ActivityStarter { if (abort) { if (resultRecord != null) { resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, - Activity.RESULT_CANCELED, null); + RESULT_CANCELED, null); } // We pretend to the caller that it was really started, but // they will just get a cancel result. ActivityOptions.abort(options); - return ActivityManager.START_SUCCESS; + return START_SUCCESS; } // If permissions need a review before any of the app components can run, we @@ -429,8 +525,8 @@ public class ActivityStarter { doPendingActivityLaunchesLocked(false); - err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, - voiceInteractor, startFlags, true, options, inTask); + err = startActivityUnchecked( + r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask); if (err < 0) { // If someone asked to have the keyguard dismissed on the next @@ -608,7 +704,7 @@ public class ActivityStarter { } catch (InterruptedException e) { } } while (!outResult.timeout && outResult.who == null); - } else if (res == ActivityManager.START_TASK_TO_FRONT) { + } else if (res == START_TASK_TO_FRONT) { ActivityRecord r = stack.topRunningActivityLocked(); if (r.nowVisible && r.state == RESUMED) { outResult.timeout = false; @@ -632,119 +728,383 @@ public class ActivityStarter { } } - final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, - boolean doResume, ActivityOptions options, TaskRecord inTask) { - final Intent intent = r.intent; - final int callingUid = r.launchedFromUid; + final int startActivities(IApplicationThread caller, int callingUid, String callingPackage, + Intent[] intents, String[] resolvedTypes, IBinder resultTo, + Bundle bOptions, int userId) { + if (intents == null) { + throw new NullPointerException("intents is null"); + } + if (resolvedTypes == null) { + throw new NullPointerException("resolvedTypes is null"); + } + if (intents.length != resolvedTypes.length) { + throw new IllegalArgumentException("intents are length different than resolvedTypes"); + } - final Rect newBounds = mSupervisor.getOverrideBounds(r, options, inTask); - final boolean overrideBounds = newBounds != null; - // In some flows in to this function, we retrieve the task record and hold on to it - // without a lock before calling back in to here... so the task at this point may - // not actually be in recents. Check for that, and if it isn't in recents just - // consider it invalid. - if (inTask != null && !inTask.inRecents) { - Slog.w(TAG, "Starting activity in task not in recents: " + inTask); - inTask = null; - } - - final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP; - final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE; - final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK; - int launchFlags = adjustLaunchFlagsToDocumentMode(r, launchSingleInstance, launchSingleTask, - intent.getFlags()); - final boolean launchTaskBehind = r.mLaunchTaskBehind - && !launchSingleTask && !launchSingleInstance - && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0; - - if (r.resultTo != null && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 - && r.resultTo.task.stack != null) { - // For whatever reason this activity is being launched into a new - // task... yet the caller has requested a result back. Well, that - // is pretty messed up, so instead immediately send back a cancel - // and let the new task continue launched as normal without a - // dependency on its originator. - Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); - r.resultTo.task.stack.sendActivityResultLocked(-1, - r.resultTo, r.resultWho, r.requestCode, - Activity.RESULT_CANCELED, null); - r.resultTo = null; + int callingPid; + if (callingUid >= 0) { + callingPid = -1; + } else if (caller == null) { + callingPid = Binder.getCallingPid(); + callingUid = Binder.getCallingUid(); + } else { + callingPid = callingUid = -1; } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mService) { + ActivityRecord[] outActivity = new ActivityRecord[1]; + for (int i=0; i<intents.length; i++) { + Intent intent = intents[i]; + if (intent == null) { + continue; + } + + // Refuse possible leaked file descriptors + if (intent != null && intent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + boolean componentSpecified = intent.getComponent() != null; + + // Don't modify the client's object! + intent = new Intent(intent); - if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) { - launchFlags |= FLAG_ACTIVITY_NEW_TASK; + // Collect information about the target of the Intent. + ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0, + null, userId); + // TODO: New, check if this is correct + aInfo = mService.getActivityInfoForUser(aInfo, userId); + + if (aInfo != null && + (aInfo.applicationInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { + throw new IllegalArgumentException( + "FLAG_CANT_SAVE_STATE not supported here"); + } + + ActivityOptions options = ActivityOptions.fromBundle( + i == intents.length - 1 ? bOptions : null); + int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/, + resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1, + callingPid, callingUid, callingPackage, callingPid, callingUid, 0, + options, false, componentSpecified, outActivity, null, null); + if (res < 0) { + return res; + } + + resultTo = outActivity[0] != null ? outActivity[0].appToken : null; + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + + return START_SUCCESS; + } + + private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) { + + setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, + voiceInteractor); + + computeLaunchingTaskFlags(); + + computeSourceStack(); + + mIntent.setFlags(mLaunchFlags); + + ActivityRecord intentActivity = getReusableIntentActivity(); + + if (intentActivity != null) { + // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but + // still needs to be a lock task mode violation since the task gets cleared out and + // the device would otherwise leave the locked task. + if (mSupervisor.isLockTaskModeViolation(intentActivity.task, + (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) + == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { + mSupervisor.showLockTaskToast(); + Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode"); + return START_RETURN_LOCK_TASK_MODE_VIOLATION; + } + + if (mStartActivity.task == null) { + mStartActivity.task = intentActivity.task; + } + if (intentActivity.task.intent == null) { + // This task was started because of movement of the activity based on affinity... + // Now that we are actually launching it, we can assign the base intent. + intentActivity.task.setIntent(mStartActivity); + } + + intentActivity = setTargetStackAndMoveToFrontIfNeeded(intentActivity); + + if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { + // We don't need to start a new activity, and the client said not to do anything + // if that is the case, so this is it! And for paranoia, make sure we have + // correctly resumed the top activity. + resumeTargetStackIfNeeded(); + return START_RETURN_INTENT_TO_CALLER; + } + + setTaskFromIntentActivity(intentActivity); + + if (!mAddingToTask && mReuseTask == null) { + // We didn't do anything... but it was needed (a.k.a., client don't use that + // intent!) And for paranoia, make sure we have correctly resumed the top activity. + resumeTargetStackIfNeeded(); + return START_TASK_TO_FRONT; + } + } + + if (mStartActivity.packageName == null) { + if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) { + mStartActivity.resultTo.task.stack.sendActivityResultLocked( + -1, mStartActivity.resultTo, mStartActivity.resultWho, + mStartActivity.requestCode, RESULT_CANCELED, null); + } + ActivityOptions.abort(mOptions); + return START_CLASS_NOT_FOUND; + } + + // If the activity being launched is the same as the one currently at the top, then + // we need to check if it should only be launched once. + final ActivityStack topStack = mSupervisor.mFocusedStack; + final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); + final boolean dontStart = top != null && mStartActivity.resultTo == null + && top.realActivity.equals(mStartActivity.realActivity) + && top.userId == mStartActivity.userId + && top.app != null && top.app.thread != null + && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 + || mLaunchSingleTop || mLaunchSingleTask); + if (dontStart) { + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); + // For paranoia, make sure we have correctly resumed the top activity. + topStack.mLastPausedActivity = null; + if (mDoResume) { + mSupervisor.resumeFocusedStackTopActivityLocked(); + } + ActivityOptions.abort(mOptions); + if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { + // We don't need to start a new activity, and the client said not to do + // anything if that is the case, so this is it! + return START_RETURN_INTENT_TO_CALLER; + } + top.deliverNewIntentLocked( + mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + return START_DELIVERED_TO_TOP; + } + + boolean newTask = false; + final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) + ? mSourceRecord.task : null; + + // Should this be considered a new task? + if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask + && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { + newTask = true; + setTaskFromReuseOrCreateNewTask(taskToAffiliate); + + if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) { + Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); + return START_RETURN_LOCK_TASK_MODE_VIOLATION; + } + if (!mMovedHome + && (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) + == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { + // Caller wants to appear on home activity, so before starting + // their own activity we will bring home to the front. + mStartActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); + } + } else if (mSourceRecord != null) { + if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) { + Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); + return START_RETURN_LOCK_TASK_MODE_VIOLATION; + } + + final int result = setTaskFromSourceRecord(); + if (result != START_SUCCESS) { + return result; + } + } else if (mInTask != null) { + // The caller is asking that the new activity be started in an explicit + // task it has provided to us. + if (mSupervisor.isLockTaskModeViolation(mInTask)) { + Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); + return START_RETURN_LOCK_TASK_MODE_VIOLATION; + } + + final int result = setTaskFromInTask(); + if (result != START_SUCCESS) { + return result; + } + } else { + // This not being started from an existing activity, and not part of a new task... + // just put it in the top task, though these days this case should never happen. + setTaskToCurrentTopOrCreateNewTask(); + } + + mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName, + mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId); + + if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) { + mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE); + } + if (newTask) { + EventLog.writeEvent( + EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId); + } + ActivityStack.logStartActivity( + EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task); + mTargetStack.mLastPausedActivity = null; + mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions); + if (mDoResume) { + if (!mLaunchTaskBehind) { + // TODO(b/26381750): Remove this code after verification that all the decision + // points above moved targetStack to the front which will also set the focus + // activity. + mService.setFocusedActivityLocked(mStartActivity, "startedActivity"); + } + mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, + mOptions); + } else { + mTargetStack.addRecentActivityLocked(mStartActivity); + } + mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + + if (!mStartActivity.task.mResizeable + && mSupervisor.isStackDockedInEffect(mTargetStack.mStackId)) { + mSupervisor.showNonResizeableDockToast(mStartActivity.task.taskId); + } + + return START_SUCCESS; + } + + private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, + boolean doResume, int startFlags, ActivityRecord sourceRecord, + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) { + reset(); + + mStartActivity = r; + mIntent = r.intent; + mOptions = options; + mCallingUid = r.launchedFromUid; + mSourceRecord = sourceRecord; + mVoiceSession = voiceSession; + mVoiceInteractor = voiceInteractor; + + mLaunchBounds = getOverrideBounds(r, options, inTask); + + mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP; + mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE; + mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK; + mLaunchFlags = adjustLaunchFlagsToDocumentMode( + r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); + mLaunchTaskBehind = r.mLaunchTaskBehind + && !mLaunchSingleTask && !mLaunchSingleInstance + && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0; + + sendNewTaskResultRequestIfNeeded(); + + if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) { + mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; } // If we are actually going to launch in to a new task, there are some cases where // we further want to do multiple task. - if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { - if (launchTaskBehind - || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) { - launchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK; + if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { + if (mLaunchTaskBehind + || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) { + mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK; } } // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. - mSupervisor.mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; + mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0; if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving); // If the caller has asked not to resume at this point, we make note // of this in the record so that we can skip it when trying to find // the top running activity. + mDoResume = doResume; if (!doResume || !mSupervisor.okToShowLocked(r)) { r.delayedResume = true; - doResume = false; + mDoResume = false; } - final ActivityRecord notTop = - (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; + mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null; + + mInTask = inTask; + // In some flows in to this function, we retrieve the task record and hold on to it + // without a lock before calling back in to here... so the task at this point may + // not actually be in recents. Check for that, and if it isn't in recents just + // consider it invalid. + if (inTask != null && !inTask.inRecents) { + Slog.w(TAG, "Starting activity in task not in recents: " + inTask); + mInTask = null; + } - // If the onlyIfNeeded flag is set, then we can do this if the activity - // being launched is the same as the one making the call... or, as - // a special case, if we do not know the caller then we count the - // current top activity as the caller. - if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { + mStartFlags = startFlags; + // If the onlyIfNeeded flag is set, then we can do this if the activity being launched + // is the same as the one making the call... or, as a special case, if we do not know + // the caller then we count the current top activity as the caller. + if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { ActivityRecord checkedCaller = sourceRecord; if (checkedCaller == null) { checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked( - notTop); + mNotTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { // Caller is not the same as launcher, so always needed. - startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED; + mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED; } } - boolean addingToTask = false; - TaskRecord reuseTask = null; + mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; + } + + private void sendNewTaskResultRequestIfNeeded() { + if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 + && mStartActivity.resultTo.task.stack != null) { + // For whatever reason this activity is being launched into a new task... + // yet the caller has requested a result back. Well, that is pretty messed up, + // so instead immediately send back a cancel and let the new task continue launched + // as normal without a dependency on its originator. + Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); + mStartActivity.resultTo.task.stack.sendActivityResultLocked(-1, mStartActivity.resultTo, + mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, null); + mStartActivity.resultTo = null; + } + } - // If the caller is not coming from another activity, but has given us an - // explicit task into which they would like us to launch the new activity, - // then let's see about doing that. - if (sourceRecord == null && inTask != null && inTask.stack != null) { - final Intent baseIntent = inTask.getBaseIntent(); - final ActivityRecord root = inTask.getRootActivity(); + private void computeLaunchingTaskFlags() { + // If the caller is not coming from another activity, but has given us an explicit task into + // which they would like us to launch the new activity, then let's see about doing that. + if (mSourceRecord == null && mInTask != null && mInTask.stack != null) { + final Intent baseIntent = mInTask.getBaseIntent(); + final ActivityRecord root = mInTask.getRootActivity(); if (baseIntent == null) { - ActivityOptions.abort(options); + ActivityOptions.abort(mOptions); throw new IllegalArgumentException("Launching into task without base intent: " - + inTask); + + mInTask); } // If this task is empty, then we are adding the first activity -- it // determines the root, and must be launching as a NEW_TASK. - if (launchSingleInstance || launchSingleTask) { - if (!baseIntent.getComponent().equals(r.intent.getComponent())) { - ActivityOptions.abort(options); + if (mLaunchSingleInstance || mLaunchSingleTask) { + if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) { + ActivityOptions.abort(mOptions); throw new IllegalArgumentException("Trying to launch singleInstance/Task " - + r + " into different task " + inTask); + + mStartActivity + " into different task " + mInTask); } if (root != null) { - ActivityOptions.abort(options); - throw new IllegalArgumentException("Caller with inTask " + inTask + ActivityOptions.abort(mOptions); + throw new IllegalArgumentException("Caller with mInTask " + mInTask + " has root " + root + " but target is singleInstance/Task"); } } @@ -752,533 +1112,405 @@ public class ActivityStarter { // If task is empty, then adopt the interesting intent launch flags in to the // activity being started. if (root == null) { - final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK - | FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT - | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; - launchFlags = (launchFlags&~flagsOfInterest) - | (baseIntent.getFlags()&flagsOfInterest); - intent.setFlags(launchFlags); - inTask.setIntent(r); - addingToTask = true; - - // If the task is not empty and the caller is asking to start it as the root - // of a new task, then we don't actually want to start this on the task. We - // will bring the task to the front, and possibly give it a new intent. - } else if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { - addingToTask = false; + final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK + | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS; + mLaunchFlags = (mLaunchFlags & ~flagsOfInterest) + | (baseIntent.getFlags() & flagsOfInterest); + mIntent.setFlags(mLaunchFlags); + mInTask.setIntent(mStartActivity); + mAddingToTask = true; + + // If the task is not empty and the caller is asking to start it as the root of + // a new task, then we don't actually want to start this on the task. We will + // bring the task to the front, and possibly give it a new intent. + } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { + mAddingToTask = false; } else { - addingToTask = true; + mAddingToTask = true; } - reuseTask = inTask; + mReuseTask = mInTask; } else { - inTask = null; - // Launch ResolverActivity in the source task, so that it stays in the task - // bounds when in freeform workspace. - // Also put noDisplay activities in the source task. These by itself can - // be placed in any task/stack, however it could launch other activities - // like ResolverActivity, and we want those to stay in the original task. - if ((r.isResolverActivity() || r.noDisplay) && sourceRecord != null - && sourceRecord.isFreeform()) { - addingToTask = true; + mInTask = null; + // Launch ResolverActivity in the source task, so that it stays in the task bounds + // when in freeform workspace. + // Also put noDisplay activities in the source task. These by itself can be placed + // in any task/stack, however it could launch other activities like ResolverActivity, + // and we want those to stay in the original task. + if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null + && mSourceRecord.isFreeform()) { + mAddingToTask = true; } } - if (inTask == null) { - if (sourceRecord == null) { + if (mInTask == null) { + if (mSourceRecord == null) { // This activity is not being started from another... in this // case we -always- start a new task. - if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) { + if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) { Slog.w(TAG, "startActivity called from non-Activity context; forcing " + - "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent); - launchFlags |= FLAG_ACTIVITY_NEW_TASK; + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); + mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; } - } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) { // The original activity who is starting us is running as a single // instance... this new activity it is starting must go on its // own task. - launchFlags |= FLAG_ACTIVITY_NEW_TASK; - } else if (launchSingleInstance || launchSingleTask) { + mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; + } else if (mLaunchSingleInstance || mLaunchSingleTask) { // The activity being started is a single instance... it always // gets launched into its own task. - launchFlags |= FLAG_ACTIVITY_NEW_TASK; + mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; } } + } - ActivityInfo newTaskInfo = null; - Intent newTaskIntent = null; - final ActivityStack sourceStack; - if (sourceRecord != null) { - if (sourceRecord.finishing) { - // If the source is finishing, we can't further count it as our source. This - // is because the task it is associated with may now be empty and on its way out, - // so we don't want to blindly throw it in to that task. Instead we will take - // the NEW_TASK flow and try to find a task for it. But save the task information - // so it can be used when creating the new task. - if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) { - Slog.w(TAG, "startActivity called from finishing " + sourceRecord - + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent); - launchFlags |= FLAG_ACTIVITY_NEW_TASK; - newTaskInfo = sourceRecord.info; - newTaskIntent = sourceRecord.task.intent; - } - sourceRecord = null; - sourceStack = null; - } else { - sourceStack = sourceRecord.task.stack; - } - } else { - sourceStack = null; + private void computeSourceStack() { + if (mSourceRecord == null) { + mSourceStack = null; + return; + } + if (!mSourceRecord.finishing) { + mSourceStack = mSourceRecord.task.stack; + return; } - boolean movedHome = false; - ActivityStack targetStack; - - intent.setFlags(launchFlags); - final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0; + // If the source is finishing, we can't further count it as our source. This is because the + // task it is associated with may now be empty and on its way out, so we don't want to + // blindly throw it in to that task. Instead we will take the NEW_TASK flow and try to find + // a task for it. But save the task information so it can be used when creating the new task. + if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) { + Slog.w(TAG, "startActivity called from finishing " + mSourceRecord + + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); + mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; + mNewTaskInfo = mSourceRecord.info; + mNewTaskIntent = mSourceRecord.task.intent; + } + mSourceRecord = null; + mSourceStack = null; + } - ActivityRecord intentActivity = getReusableIntentActivity(r, inTask, intent, - launchSingleInstance, launchSingleTask, launchFlags); - if (intentActivity != null) { - // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused - // but still needs to be a lock task mode violation since the task gets - // cleared out and the device would otherwise leave the locked task. - if (mSupervisor.isLockTaskModeViolation(intentActivity.task, - (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) - == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { - mSupervisor.showLockTaskToast(); - Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode"); - return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; - } - if (r.task == null) { - r.task = intentActivity.task; - } - if (intentActivity.task.intent == null) { - // This task was started because of movement of the activity based on affinity... - // Now that we are actually launching it, we can assign the base intent. - intentActivity.task.setIntent(r); - } + /** + * Decide whether the new activity should be inserted into an existing task. Returns null + * if not or an ActivityRecord with the task into which the new activity should be added. + */ + private ActivityRecord getReusableIntentActivity() { + // We may want to try to place the new activity in to an existing task. We always + // do this if the target activity is singleTask or singleInstance; we will also do + // this if NEW_TASK has been requested, and there is not an additional qualifier telling + // us to still place it in a new task: multi task, always doc mode, or being asked to + // launch this as a new task behind the current one. + boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 && + (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0) + || mLaunchSingleInstance || mLaunchSingleTask; + // If bring to front is requested, and no result is requested and we have not been given + // an explicit task to launch in to, and we can find a task that was started with this + // same component, then instead of launching bring that one to the front. + putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; + ActivityRecord intentActivity = null; + if (putIntoExistingTask) { + // See if there is a task to bring to the front. If this is a SINGLE_INSTANCE + // activity, there can be one and only one instance of it in the history, and it is + // always in its own unique task, so we do a special search. + intentActivity = mLaunchSingleInstance ? mSupervisor.findActivityLocked(mIntent, mStartActivity.info) + : mSupervisor.findTaskLocked(mStartActivity); + } + return intentActivity; + } - targetStack = intentActivity.task.stack; - targetStack.mLastPausedActivity = null; - // If the target task is not in the front, then we need - // to bring it to the front... except... well, with - // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like - // to have the same behavior as if a new instance was - // being started, which means not bringing it to the front - // if the caller is not itself in the front. - final ActivityStack focusStack = mSupervisor.getFocusedStack(); - ActivityRecord curTop = (focusStack == null) - ? null : focusStack.topRunningNonDelayedActivityLocked(notTop); - boolean movedToFront = false; - if (curTop != null && (curTop.task != intentActivity.task || - curTop.task != focusStack.topTask())) { - r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - if (sourceRecord == null || (sourceStack.topActivity() != null && - sourceStack.topActivity().task == sourceRecord.task)) { - // We really do want to push this one into the user's face, right now. - if (launchTaskBehind && sourceRecord != null) { - intentActivity.setTaskToAffiliateWith(sourceRecord.task); - } - movedHome = true; - final ActivityStack sideStack = getLaunchToSideStack(r, launchFlags, r.task); - if (sideStack == null || sideStack == targetStack) { - // We only want to move to the front, if we aren't going to launch on a - // different stack. If we launch on a different stack, we will put the - // task on top there. - targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation, - options, r.appTimeTracker, "bringingFoundTaskToFront"); - movedToFront = true; - } - if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) - == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { - // Caller wants to appear on home activity. - intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); - } - options = null; + private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) { + mTargetStack = intentActivity.task.stack; + mTargetStack.mLastPausedActivity = null; + // If the target task is not in the front, then we need to bring it to the front... + // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have + // the same behavior as if a new instance was being started, which means not bringing it + // to the front if the caller is not itself in the front. + final ActivityStack focusStack = mSupervisor.getFocusedStack(); + ActivityRecord curTop = (focusStack == null) + ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); + + if (curTop != null && (curTop.task != intentActivity.task || + curTop.task != focusStack.topTask())) { + mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + if (mSourceRecord == null || (mSourceStack.topActivity() != null && + mSourceStack.topActivity().task == mSourceRecord.task)) { + // We really do want to push this one into the user's face, right now. + if (mLaunchTaskBehind && mSourceRecord != null) { + intentActivity.setTaskToAffiliateWith(mSourceRecord.task); } - } - if (!movedToFront && doResume) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack - + " from " + intentActivity); - targetStack.moveToFront("intentActivityFound"); - } - - // If the caller has requested that the target task be - // reset, then do so. - if ((launchFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { - intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r); - } - if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { - // We don't need to start a new activity, and - // the client said not to do anything if that - // is the case, so this is it! And for paranoia, make - // sure we have correctly resumed the top activity. - if (doResume) { - mSupervisor.resumeTopActivitiesLocked(targetStack, null, options); - - // Make sure to notify Keyguard as well if we are not running an app - // transition later. - if (!movedToFront) { - mSupervisor.notifyActivityDrawnForKeyguard(); - } - } else { - ActivityOptions.abort(options); + mMovedHome = true; + final ActivityStack launchStack = + getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, + mOptions, true); + if (launchStack == null || launchStack == mTargetStack) { + // We only want to move to the front, if we aren't going to launch on a + // different stack. If we launch on a different stack, we will put the + // task on top there. + mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation, + mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); + mMovedToFront = true; + } + if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) + == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { + // Caller wants to appear on home activity. + intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); } - mSupervisor.updateUserStackLocked(r.userId, targetStack); - return ActivityManager.START_RETURN_INTENT_TO_CALLER; + mOptions = null; } - if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) - == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) { - // The caller has requested to completely replace any - // existing task with its new activity. Well that should - // not be too hard... - reuseTask = intentActivity.task; - reuseTask.performClearTaskLocked(); - reuseTask.setIntent(r); - } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 - || launchSingleInstance || launchSingleTask) { - // In this situation we want to remove all activities - // from the task up to the one being started. In most - // cases this means we are resetting the task to its - // initial state. - ActivityRecord top = intentActivity.task.performClearTaskLocked(r, launchFlags); - if (top != null) { - if (top.frontOfTask) { - // Activity aliases may mean we use different - // intents for the top activity, so make sure - // the task now has the identity of the new - // intent. - top.task.setIntent(r); - } - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); - } else { - // A special case: we need to start the activity because it is not - // currently running, and the caller has asked to clear the current - // task to have this activity at the top. - addingToTask = true; - // Now pretend like this activity is being started by the top of its - // task, so it is put in the right place. - sourceRecord = intentActivity; - TaskRecord task = sourceRecord.task; - if (task != null && task.stack == null) { - // Target stack got cleared when we all activities were removed - // above. Go ahead and reset it. - targetStack = computeStackFocus( - sourceRecord, false /* newTask */, null /* bounds */, launchFlags); - targetStack.addTask(task, - !launchTaskBehind /* toTop */, "startActivityUnchecked"); - } + } + if (!mMovedToFront && mDoResume) { + if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack + + " from " + intentActivity); + mTargetStack.moveToFront("intentActivityFound"); + } + + // If the caller has requested that the target task be reset, then do so. + if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { + return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity); + } + return intentActivity; + } + private void setTaskFromIntentActivity(ActivityRecord intentActivity) { + if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) + == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) { + // The caller has requested to completely replace any existing task with its new + // activity. Well that should not be too hard... + mReuseTask = intentActivity.task; + mReuseTask.performClearTaskLocked(); + mReuseTask.setIntent(mStartActivity); + } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 + || mLaunchSingleInstance || mLaunchSingleTask) { + // In this situation we want to remove all activities from the task up to the one + // being started. In most cases this means we are resetting the task to its initial + // state. + ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity, + mLaunchFlags); + if (top != null) { + if (top.frontOfTask) { + // Activity aliases may mean we use different intents for the top activity, + // so make sure the task now has the identity of the new intent. + top.task.setIntent(mStartActivity); } - } else if (r.realActivity.equals(intentActivity.task.realActivity)) { - // In this case the top activity on the task is the - // same as the one being launched, so we take that - // as a request to bring the task to the foreground. - // If the top activity in the task is the root - // activity, deliver this new intent to it if it - // desires. - if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop) - && intentActivity.realActivity.equals(r.realActivity)) { - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, - intentActivity.task); - if (intentActivity.frontOfTask) { - intentActivity.task.setIntent(r); - } - intentActivity.deliverNewIntentLocked(callingUid, r.intent, - r.launchedFromPackage); - } else if (!r.intent.filterEquals(intentActivity.task.intent)) { - // In this case we are launching the root activity - // of the task, but with a different intent. We - // should start a new instance on top. - addingToTask = true; - sourceRecord = intentActivity; + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, top.task); + top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + } else { + // A special case: we need to start the activity because it is not currently + // running, and the caller has asked to clear the current task to have this + // activity at the top. + mAddingToTask = true; + // Now pretend like this activity is being started by the top of its task, so it + // is put in the right place. + mSourceRecord = intentActivity; + final TaskRecord task = mSourceRecord.task; + if (task != null && task.stack == null) { + // Target stack got cleared when we all activities were removed above. + // Go ahead and reset it. + mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */, + null /* bounds */, mLaunchFlags, mOptions); + mTargetStack.addTask(task, + !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } - } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { - // In this case an activity is being launched in to an - // existing task, without resetting that task. This - // is typically the situation of launching an activity - // from a notification or shortcut. We want to place - // the new activity on top of the current task. - addingToTask = true; - sourceRecord = intentActivity; - } else if (!intentActivity.task.rootWasReset) { - // In this case we are launching in to an existing task - // that has not yet been started from its front door. - // The current task has been brought to the front. - // Ideally, we'd probably like to place this new task - // at the bottom of its stack, but that's a little hard - // to do with the current organization of the code so - // for now we'll just drop it. - intentActivity.task.setIntent(r); } - if (!addingToTask && reuseTask == null) { - // We didn't do anything... but it was needed (a.k.a., client - // don't use that intent!) And for paranoia, make - // sure we have correctly resumed the top activity. - if (doResume) { - targetStack.resumeTopActivityLocked(null, options); - if (!movedToFront) { - // Make sure to notify Keyguard as well if we are not running an app - // transition later. - mSupervisor.notifyActivityDrawnForKeyguard(); - } - } else { - ActivityOptions.abort(options); + } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) { + // In this case the top activity on the task is the same as the one being launched, + // so we take that as a request to bring the task to the foreground. If the top + // activity in the task is the root activity, deliver this new intent to it if it + // desires. + if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) + && intentActivity.realActivity.equals(mStartActivity.realActivity)) { + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, + intentActivity.task); + if (intentActivity.frontOfTask) { + intentActivity.task.setIntent(mStartActivity); } - mSupervisor.updateUserStackLocked(r.userId, targetStack); - return ActivityManager.START_TASK_TO_FRONT; + intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, + mStartActivity.launchedFromPackage); + } else if (!mStartActivity.intent.filterEquals(intentActivity.task.intent)) { + // In this case we are launching the root activity of the task, but with a + // different intent. We should start a new instance on top. + mAddingToTask = true; + mSourceRecord = intentActivity; } + } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { + // In this case an activity is being launched in to an existing task, without + // resetting that task. This is typically the situation of launching an activity + // from a notification or shortcut. We want to place the new activity on top of the + // current task. + mAddingToTask = true; + mSourceRecord = intentActivity; + } else if (!intentActivity.task.rootWasReset) { + // In this case we are launching into an existing task that has not yet been started + // from its front door. The current task has been brought to the front. Ideally, + // we'd probably like to place this new task at the bottom of its stack, but that's + // a little hard to do with the current organization of the code so for now we'll + // just drop it. + intentActivity.task.setIntent(mStartActivity); } + } - //String uri = r.intent.toURI(); - //Intent intent2 = new Intent(uri); - //Slog.i(TAG, "Given intent: " + r.intent); - //Slog.i(TAG, "URI is: " + uri); - //Slog.i(TAG, "To intent: " + intent2); - - if (r.packageName != null) { - // If the activity being launched is the same as the one currently - // at the top, then we need to check if it should only be launched - // once. - ActivityStack topStack = mSupervisor.mFocusedStack; - ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop); - final boolean dontStart = top != null && r.resultTo == null - && top.realActivity.equals(r.realActivity) && top.userId == r.userId - && top.app != null && top.app.thread != null - && ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 - || launchSingleTop || launchSingleTask); - if (dontStart) { - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); - // For paranoia, make sure we have correctly resumed the top activity. - topStack.mLastPausedActivity = null; - if (doResume) { - mSupervisor.resumeTopActivitiesLocked(); - } - ActivityOptions.abort(options); - if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { - // We don't need to start a new activity, and the client said not to do - // anything if that is the case, so this is it! - return ActivityManager.START_RETURN_INTENT_TO_CALLER; - } - top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); - return ActivityManager.START_DELIVERED_TO_TOP; + private void resumeTargetStackIfNeeded() { + if (mDoResume) { + mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, null, mOptions); + if (!mMovedToFront) { + // Make sure to notify Keyguard as well if we are not running an app transition + // later. + mSupervisor.notifyActivityDrawnForKeyguard(); } } else { - if (r.resultTo != null && r.resultTo.task.stack != null) { - r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho, - r.requestCode, Activity.RESULT_CANCELED, null); - } - ActivityOptions.abort(options); - return ActivityManager.START_CLASS_NOT_FOUND; + ActivityOptions.abort(mOptions); } + mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + } - boolean newTask = false; - boolean keepCurTransition = false; - - final TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ? - sourceRecord.task : null; + private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) { + mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags, + mOptions); + if (mDoResume) { + mTargetStack.moveToFront("startingNewTask"); + } - // Should this be considered a new task? - if (r.resultTo == null && inTask == null && !addingToTask - && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { - newTask = true; - targetStack = computeStackFocus(r, newTask, newBounds, launchFlags); - if (doResume) { - targetStack.moveToFront("startingNewTask"); + if (mReuseTask == null) { + final TaskRecord task = mTargetStack.createTaskRecord(mSupervisor.getNextTaskId(), + mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, + mNewTaskIntent != null ? mNewTaskIntent : mIntent, + mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */); + mStartActivity.setTask(task, taskToAffiliate); + if (mLaunchBounds != null) { + mStartActivity.task.updateOverrideConfiguration(mLaunchBounds); } + if (DEBUG_TASKS) Slog.v(TAG_TASKS, + "Starting new activity " + + mStartActivity + " in new task " + mStartActivity.task); + } else { + mStartActivity.setTask(mReuseTask, taskToAffiliate); + } + } - if (reuseTask == null) { - r.setTask(targetStack.createTaskRecord(mSupervisor.getNextTaskId(), - newTaskInfo != null ? newTaskInfo : r.info, - newTaskIntent != null ? newTaskIntent : intent, - voiceSession, voiceInteractor, !launchTaskBehind /* toTop */), - taskToAffiliate); - if (overrideBounds) { - r.task.updateOverrideConfiguration(newBounds); - } - if (DEBUG_TASKS) Slog.v(TAG_TASKS, - "Starting new activity " + r + " in new task " + r.task); - } else { - r.setTask(reuseTask, taskToAffiliate); - } - if (mSupervisor.isLockTaskModeViolation(r.task)) { - Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); - return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; - } - if (!movedHome) { - if ((launchFlags & - (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) - == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) { - // Caller wants to appear on home activity, so before starting - // their own activity we will bring home to the front. - r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE); - } - } - } else if (sourceRecord != null) { - final TaskRecord sourceTask = sourceRecord.task; - if (mSupervisor.isLockTaskModeViolation(sourceTask)) { - Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); - return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; - } - targetStack = null; - if (sourceTask.stack.topTask() != sourceTask) { - // We only want to allow changing stack if the target task is not the top one, - // otherwise we would move the launching task to the other side, rather than show - // two side by side. - targetStack = getLaunchToSideStack(r, launchFlags, r.task); - } - if (targetStack == null) { - targetStack = sourceTask.stack; - } else if (targetStack != sourceTask.stack) { - mSupervisor.moveTaskToStackLocked(sourceTask.taskId, targetStack.mStackId, - ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE); - } - if (doResume) { - targetStack.moveToFront("sourceStackToFront"); - } - final TaskRecord topTask = targetStack.topTask(); - if (topTask != sourceTask) { - targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options, - r.appTimeTracker, "sourceTaskToFront"); - } - if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { - // In this case, we are adding the activity to an existing - // task, but the caller has asked to clear that task if the - // activity is already running. - ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags); - keepCurTransition = true; - if (top != null) { - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); - // For paranoia, make sure we have correctly - // resumed the top activity. - targetStack.mLastPausedActivity = null; - if (doResume) { - targetStack.resumeTopActivityLocked(null); - } - ActivityOptions.abort(options); - return ActivityManager.START_DELIVERED_TO_TOP; - } - } else if (!addingToTask && - (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { - // In this case, we are launching an activity in our own task - // that may already be running somewhere in the history, and - // we want to shuffle it to the front of the stack if so. - final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r); - if (top != null) { - final TaskRecord task = top.task; - task.moveActivityToFrontLocked(top); - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task); - top.updateOptionsLocked(options); - top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); - targetStack.mLastPausedActivity = null; - if (doResume) { - targetStack.resumeTopActivityLocked(null); - } - return ActivityManager.START_DELIVERED_TO_TOP; - } - } - // An existing activity is starting this new activity, so we want - // to keep the new one in the same task as the one that is starting - // it. - r.setTask(sourceTask, null); - if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r - + " in existing task " + r.task + " from source " + sourceRecord); - - } else if (inTask != null) { - // The caller is asking that the new activity be started in an explicit - // task it has provided to us. - if (mSupervisor.isLockTaskModeViolation(inTask)) { - Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r); - return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; - } - if (overrideBounds) { - inTask.updateOverrideConfiguration(newBounds); - int stackId = inTask.getLaunchStackId(); - if (stackId != inTask.stack.mStackId) { - mSupervisor.moveTaskToStackUncheckedLocked( - inTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront"); + private int setTaskFromSourceRecord() { + final TaskRecord sourceTask = mSourceRecord.task; + // We only want to allow changing stack if the target task is not the top one, + // otherwise we would move the launching task to the other side, rather than show + // two side by side. + final boolean launchToSideAllowed = sourceTask.stack.topTask() != sourceTask; + mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, mOptions, launchToSideAllowed); + + if (mTargetStack == null) { + mTargetStack = sourceTask.stack; + } else if (mTargetStack != sourceTask.stack) { + mSupervisor.moveTaskToStackLocked(sourceTask.taskId, mTargetStack.mStackId, + ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE); + } + if (mDoResume) { + mTargetStack.moveToFront("sourceStackToFront"); + } + final TaskRecord topTask = mTargetStack.topTask(); + if (topTask != sourceTask) { + mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions, + mStartActivity.appTimeTracker, "sourceTaskToFront"); + } + if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) { + // In this case, we are adding the activity to an existing task, but the caller has + // asked to clear that task if the activity is already running. + ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags); + mKeepCurTransition = true; + if (top != null) { + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, top.task); + top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + // For paranoia, make sure we have correctly resumed the top activity. + mTargetStack.mLastPausedActivity = null; + if (mDoResume) { + mSupervisor.resumeFocusedStackTopActivityLocked(); } + ActivityOptions.abort(mOptions); + return START_DELIVERED_TO_TOP; } - targetStack = inTask.stack; - targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker, - "inTaskToFront"); - - // Check whether we should actually launch the new activity in to the task, - // or just reuse the current activity on top. - ActivityRecord top = inTask.getTopActivity(); - if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) { - if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 - || launchSingleTop || launchSingleTask) { - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); - if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { - // We don't need to start a new activity, and - // the client said not to do anything if that - // is the case, so this is it! - return ActivityManager.START_RETURN_INTENT_TO_CALLER; - } - top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); - return ActivityManager.START_DELIVERED_TO_TOP; + } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { + // In this case, we are launching an activity in our own task that may already be + // running somewhere in the history, and we want to shuffle it to the front of the + // stack if so. + final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity); + if (top != null) { + final TaskRecord task = top.task; + task.moveActivityToFrontLocked(top); + top.updateOptionsLocked(mOptions); + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, mStartActivity, task); + top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + mTargetStack.mLastPausedActivity = null; + if (mDoResume) { + mSupervisor.resumeFocusedStackTopActivityLocked(); } + return START_DELIVERED_TO_TOP; } - - if (!addingToTask) { - // We don't actually want to have this activity added to the task, so just - // stop here but still tell the caller that we consumed the intent. - ActivityOptions.abort(options); - return ActivityManager.START_TASK_TO_FRONT; - } - - r.setTask(inTask, null); - if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r - + " in explicit task " + r.task); - - } else { - // This not being started from an existing activity, and not part - // of a new task... just put it in the top task, though these days - // this case should never happen. - targetStack = computeStackFocus(r, newTask, null /* bounds */, launchFlags); - if (doResume) { - targetStack.moveToFront("addingToTopTask"); - } - ActivityRecord prev = targetStack.topActivity(); - r.setTask(prev != null ? prev.task : targetStack.createTaskRecord( - mSupervisor.getNextTaskId(), r.info, intent, null, null, true), null); - mWindowManager.moveTaskToTop(r.task.taskId); - if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r - + " in new guessed " + r.task); } - mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, - intent, r.getUriPermissionsLocked(), r.userId); + // An existing activity is starting this new activity, so we want to keep the new one in + // the same task as the one that is starting it. + mStartActivity.setTask(sourceTask, null); + if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + + " in existing task " + mStartActivity.task + " from source " + mSourceRecord); + return START_SUCCESS; + } - if (sourceRecord != null && sourceRecord.isRecentsActivity()) { - r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE); + private int setTaskFromInTask() { + if (mLaunchBounds != null) { + mInTask.updateOverrideConfiguration(mLaunchBounds); + int stackId = mInTask.getLaunchStackId(); + if (stackId != mInTask.stack.mStackId) { + mSupervisor.moveTaskToStackUncheckedLocked( + mInTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront"); + } } - if (newTask) { - EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId); - } - ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); - targetStack.mLastPausedActivity = null; - targetStack.startActivityLocked(r, newTask, keepCurTransition, options); - if (doResume) { - if (!launchTaskBehind) { - mService.setFocusedActivityLocked(r, "startedActivity"); + mTargetStack = mInTask.stack; + mTargetStack.moveTaskToFrontLocked( + mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront"); + + // Check whether we should actually launch the new activity in to the task, + // or just reuse the current activity on top. + ActivityRecord top = mInTask.getTopActivity(); + if (top != null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId) { + if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 + || mLaunchSingleTop || mLaunchSingleTask) { + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); + if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { + // We don't need to start a new activity, and the client said not to do + // anything if that is the case, so this is it! + return START_RETURN_INTENT_TO_CALLER; + } + top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + return START_DELIVERED_TO_TOP; } - mSupervisor.resumeTopActivitiesLocked(targetStack, r, options); - } else { - targetStack.addRecentActivityLocked(r); } - mSupervisor.updateUserStackLocked(r.userId, targetStack); - if (!r.task.mResizeable && mSupervisor.isStackDockedInEffect(targetStack.mStackId)) { - mSupervisor.showNonResizeableDockToast(r.task.taskId); + if (!mAddingToTask) { + // We don't actually want to have this activity added to the task, so just + // stop here but still tell the caller that we consumed the intent. + ActivityOptions.abort(mOptions); + return START_TASK_TO_FRONT; } - return ActivityManager.START_SUCCESS; + mStartActivity.setTask(mInTask, null); + if (DEBUG_TASKS) Slog.v(TAG_TASKS, + "Starting new activity " + mStartActivity + " in explicit task " + mStartActivity.task); + + return START_SUCCESS; + } + + private void setTaskToCurrentTopOrCreateNewTask() { + mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags, + mOptions); + if (mDoResume) { + mTargetStack.moveToFront("addingToTopTask"); + } + final ActivityRecord prev = mTargetStack.topActivity(); + final TaskRecord task = prev != null ? prev.task + : mTargetStack.createTaskRecord( + mSupervisor.getNextTaskId(), mStartActivity.info, mIntent, null, null, true); + mStartActivity.setTask(task, null); + mWindowManager.moveTaskToTop(mStartActivity.task.taskId); + if (DEBUG_TASKS) Slog.v(TAG_TASKS, + "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.task); } private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance, @@ -1308,90 +1540,13 @@ public class ActivityStarter { return launchFlags; } - final int startActivities(IApplicationThread caller, int callingUid, String callingPackage, - Intent[] intents, String[] resolvedTypes, IBinder resultTo, - Bundle bOptions, int userId) { - if (intents == null) { - throw new NullPointerException("intents is null"); - } - if (resolvedTypes == null) { - throw new NullPointerException("resolvedTypes is null"); - } - if (intents.length != resolvedTypes.length) { - throw new IllegalArgumentException("intents are length different than resolvedTypes"); - } - - - int callingPid; - if (callingUid >= 0) { - callingPid = -1; - } else if (caller == null) { - callingPid = Binder.getCallingPid(); - callingUid = Binder.getCallingUid(); - } else { - callingPid = callingUid = -1; - } - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mService) { - ActivityRecord[] outActivity = new ActivityRecord[1]; - for (int i=0; i<intents.length; i++) { - Intent intent = intents[i]; - if (intent == null) { - continue; - } - - // Refuse possible leaked file descriptors - if (intent != null && intent.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); - } - - boolean componentSpecified = intent.getComponent() != null; - - // Don't modify the client's object! - intent = new Intent(intent); - - // Collect information about the target of the Intent. - ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0, - null, userId); - // TODO: New, check if this is correct - aInfo = mService.getActivityInfoForUser(aInfo, userId); - - if (aInfo != null && - (aInfo.applicationInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { - throw new IllegalArgumentException( - "FLAG_CANT_SAVE_STATE not supported here"); - } - - ActivityOptions options = ActivityOptions.fromBundle( - i == intents.length - 1 ? bOptions : null); - int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/, - resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1, - callingPid, callingUid, callingPackage, callingPid, callingUid, 0, - options, false, componentSpecified, outActivity, null, null); - if (res < 0) { - return res; - } - - resultTo = outActivity[0] != null ? outActivity[0].appToken : null; - } - } - } finally { - Binder.restoreCallingIdentity(origId); - } - - return ActivityManager.START_SUCCESS; - } - final void doPendingActivityLaunchesLocked(boolean doResume) { while (!mPendingActivityLaunches.isEmpty()) { PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); try { - startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, - pal.startFlags, doResume && mPendingActivityLaunches.isEmpty(), - null, null); + startActivityUnchecked(pal.r, pal.sourceRecord, null, null, pal.startFlags, + doResume && mPendingActivityLaunches.isEmpty(), null, null); } catch (Exception e) { Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e); pal.sendErrorResult(e.getMessage()); @@ -1400,13 +1555,13 @@ public class ActivityStarter { } private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds, - int launchFlags) { + int launchFlags, ActivityOptions aOptions) { final TaskRecord task = r.task; if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) { return mSupervisor.mHomeStack; } - ActivityStack stack = getLaunchToSideStack(r, launchFlags, task); + ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions, true); if (stack != null) { return stack; } @@ -1442,7 +1597,7 @@ public class ActivityStarter { final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID || focusedStackId == DOCKED_STACK_ID - || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable); + || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeable()); if (canUseFocusedStack && (!newTask || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, @@ -1471,10 +1626,19 @@ public class ActivityStarter { return stack; } - private ActivityStack getLaunchToSideStack(ActivityRecord r, int launchFlags, TaskRecord task) { - if ((launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) { + private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task, + ActivityOptions aOptions, boolean launchToSideAllowed) { + final int launchStackId = + (aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID; + + if (isValidLaunchStackId(launchStackId, r)) { + return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP); + } + + if (!launchToSideAllowed || (launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) { return null; } + // The parent activity doesn't want to launch the activity on top of itself, but // instead tries to put it onto other side in side-by-side mode. final ActivityStack parentStack = task != null ? task.stack @@ -1498,36 +1662,39 @@ public class ActivityStarter { } } - /** - * Decide whether the new activity should be inserted into an existing task. Returns null if not - * or an ActivityRecord with the task into which the new activity should be added. - */ - private ActivityRecord getReusableIntentActivity(ActivityRecord r, TaskRecord inTask, - Intent intent, boolean launchSingleInstance, boolean launchSingleTask, - int launchFlags) { - // We may want to try to place the new activity in to an existing task. We always - // do this if the target activity is singleTask or singleInstance; we will also do - // this if NEW_TASK has been requested, and there is not an additional qualifier telling - // us to still place it in a new task: multi task, always doc mode, or being asked to - // launch this as a new task behind the current one. - boolean putIntoExistingTask = ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 && - (launchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0) - || launchSingleInstance || launchSingleTask; - // If bring to front is requested, and no result is requested and we have not - // been given an explicit task to launch in to, and - // we can find a task that was started with this same - // component, then instead of launching bring that one to the front. - putIntoExistingTask &= inTask == null && r.resultTo == null; - ActivityRecord intentActivity = null; - if (putIntoExistingTask) { - // See if there is a task to bring to the front. If this is - // a SINGLE_INSTANCE activity, there can be one and only one - // instance of it in the history, and it is always in its own - // unique task, so we do a special search. - intentActivity = launchSingleInstance ? mSupervisor.findActivityLocked(intent, r.info) - : mSupervisor.findTaskLocked(r); + private boolean isValidLaunchStackId(int stackId, ActivityRecord r) { + if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID + || !StackId.isStaticStack(stackId)) { + return false; } - return intentActivity; + + final boolean resizeable = r.isResizeable() || mService.mForceResizableActivities; + + if (stackId != FULLSCREEN_WORKSPACE_STACK_ID && !resizeable) { + return false; + } + + if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) { + return false; + } + + final boolean supportsPip = mService.mSupportsPictureInPicture + && (r.supportsPictureInPicture() || mService.mForceResizableActivities); + if (stackId == PINNED_STACK_ID && !supportsPip) { + return false; + } + return true; + } + + Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) { + Rect newBounds = null; + if (options != null && (r.isResizeable() || (inTask != null && inTask.mResizeable))) { + if (mSupervisor.canUseActivityOptionsLaunchBounds( + options, options.getLaunchStackId())) { + newBounds = options.getLaunchBounds(); + } + } + return newBounds; } void setWindowManager(WindowManagerService wm) { diff --git a/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java new file mode 100644 index 000000000000..ff395895e638 --- /dev/null +++ b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.graphics.Rect; +import android.os.Handler; + +import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; + +/** + * When resizing the docked stack, a caller can temporarily supply task bounds that are different + * from the stack bounds. In order to return to a sane state if the caller crashes or has a bug, + * this class manages this cycle. + */ +class ResizeDockedStackTimeout { + + private static final long TIMEOUT_MS = 10 * 1000; + private final ActivityManagerService mService; + private final ActivityStackSupervisor mSupervisor; + private final Handler mHandler; + private final Rect mCurrentDockedBounds = new Rect(); + + private final Runnable mTimeoutRunnable = new Runnable() { + @Override + public void run() { + synchronized (mService) { + mSupervisor.resizeDockedStackLocked(mCurrentDockedBounds, null, null, null, null, + PRESERVE_WINDOWS); + } + } + }; + + ResizeDockedStackTimeout(ActivityManagerService service, ActivityStackSupervisor supervisor, + Handler handler) { + mService = service; + mSupervisor = supervisor; + mHandler = handler; + } + + void notifyResizing(Rect dockedBounds, boolean hasTempBounds) { + mHandler.removeCallbacks(mTimeoutRunnable); + if (!hasTempBounds) { + return; + } + mCurrentDockedBounds.set(dockedBounds); + mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS); + } + +} diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 4647d77bef89..c97d09c2ebbd 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -23,6 +23,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; +import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; @@ -441,7 +442,7 @@ final class TaskRecord { } else { autoRemoveRecents = false; } - mResizeable = info.resizeable || mService.mForceResizableActivities; + mResizeable = (info.flags & FLAG_RESIZEABLE) != 0 || mService.mForceResizableActivities; mLockTaskMode = info.lockTaskLaunchMode; mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0; setLockTaskAuth(); @@ -697,7 +698,7 @@ final class TaskRecord { if (mActivities.isEmpty()) { taskType = r.mActivityType; if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) { - mResizeable = r.info.resizeable; + mResizeable = r.isResizeable(); } isPersistable = r.isPersistable(); mCallingUid = r.launchedFromUid; @@ -1321,7 +1322,12 @@ final class TaskRecord { } } - void reportPictureInPictureModeChange() { + void reportPictureInPictureModeChangeIfNeeded(ActivityStack prevStack) { + if (prevStack == null || prevStack == stack + || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) { + return; + } + for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mActivities.get(i); if (r.app != null && r.app.thread != null) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 20b1e6058224..f2c5206f2e1b 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -304,7 +304,10 @@ final class UserController { + relatedUserId); // We still need to stop the requested user if it's a force stop. if (force) { + Slog.i(TAG, + "Force stop user " + userId + ". Related users will not be stopped"); stopSingleUserLocked(userId, callback); + return USER_OP_SUCCESS; } return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; } @@ -931,7 +934,7 @@ final class UserController { if (homeInFront) { mService.startHomeActivityLocked(newUserId, "moveUserToForeground"); } else { - mService.mStackSupervisor.resumeTopActivitiesLocked(); + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); } EventLogTags.writeAmSwitchUser(newUserId); getUserManager().onUserForeground(newUserId); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index c5e6e7c366c3..4eabe3684e4f 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -161,8 +161,9 @@ public class JobSchedulerService extends com.android.server.SystemService private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - Slog.d(TAG, "Receieved: " + intent.getAction()); - if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + final String action = intent.getAction(); + Slog.d(TAG, "Receieved: " + action); + if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { // If this is an outright uninstall rather than the first half of an // app update sequence, cancel the jobs associated with the app. if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { @@ -172,18 +173,21 @@ public class JobSchedulerService extends com.android.server.SystemService } cancelJobsForUid(uidRemoved, true); } - } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { + } else if (Intent.ACTION_USER_REMOVED.equals(action)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); if (DEBUG) { Slog.d(TAG, "Removing jobs for user: " + userId); } cancelJobsForUser(userId); - } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()) - || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) { + } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action) + || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { updateIdleMode(mPowerManager != null ? (mPowerManager.isDeviceIdleMode() || mPowerManager.isLightDeviceIdleMode()) : false); + } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { + // Kick off pending jobs for any apps that re-appeared + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } } }; @@ -425,17 +429,24 @@ public class JobSchedulerService extends com.android.server.SystemService @Override public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { - // Register br for package removals and user removals. + // Register for package removals and user removals. final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); + final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); - mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); + + final IntentFilter storageFilter = new IntentFilter(); + storageFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); + getContext().registerReceiverAsUser( + mBroadcastReceiver, UserHandle.ALL, storageFilter, null, null); + + mPowerManager = getContext().getSystemService(PowerManager.class); try { ActivityManagerNative.getDefault().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_IDLE); @@ -834,7 +845,7 @@ public class JobSchedulerService extends com.android.server.SystemService final boolean componentPresent; try { componentPresent = (AppGlobals.getPackageManager().getServiceInfo( - job.getServiceComponent(), PackageManager.MATCH_ENCRYPTION_DEFAULT, + job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId) != null); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -924,8 +935,8 @@ public class JobSchedulerService extends com.android.server.SystemService final IPackageManager pm = AppGlobals.getPackageManager(); final ComponentName service = job.getService(); try { - ServiceInfo si = pm.getServiceInfo(service, PackageManager.MATCH_ENCRYPTION_DEFAULT, - UserHandle.getUserId(uid)); + ServiceInfo si = pm.getServiceInfo(service, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid)); if (si == null) { throw new IllegalArgumentException("No such service " + service); } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 19872144bfe3..ce18818c1fba 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -121,6 +121,11 @@ public class ConditionProviders extends ManagedServices { } @Override + protected boolean checkType(IInterface service) { + return service instanceof IConditionProvider; + } + + @Override public void onBootPhaseAppsCanStart() { super.onBootPhaseAppsCanStart(); for (int i = 0; i < mSystemConditionProviders.size(); i++) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index b7662daffec4..09e66475c1be 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -141,6 +141,8 @@ abstract public class ManagedServices { abstract protected IInterface asInterface(IBinder binder); + abstract protected boolean checkType(IInterface service); + abstract protected void onServiceAdded(ManagedServiceInfo info); protected void onServiceRemovedLocked(ManagedServiceInfo removed) { } @@ -169,7 +171,8 @@ abstract public class ManagedServices { if (filter != null && !filter.matches(info.component)) continue; pw.println(" " + info.component + " (user " + info.userid + "): " + info.service - + (info.isSystem?" SYSTEM":"")); + + (info.isSystem?" SYSTEM":"") + + (info.isGuest(this)?" GUEST":"")); } } @@ -266,6 +269,18 @@ abstract public class ManagedServices { } } + /** + * Add a service to our callbacks. The lifecycle of this service is managed externally, + * but unlike a system service, it should not be considered privledged. + * */ + public void registerGuestService(ManagedServiceInfo guest) { + checkNotNull(guest.service); + checkType(guest.service); + if (registerServiceImpl(guest) != null) { + onServiceAdded(guest); + } + } + public void setCategoryState(String category, boolean enabled) { synchronized (mMutex) { final Boolean previous = mCategoryEnabled.put(category, enabled); @@ -484,7 +499,7 @@ abstract public class ManagedServices { synchronized (mMutex) { // Unbind automatically bound services, retain system services. for (ManagedServiceInfo service : mServices) { - if (!service.isSystem) { + if (!service.isSystem && !service.isGuest(this)) { toRemove.add(service); } } @@ -709,11 +724,15 @@ abstract public class ManagedServices { private ManagedServiceInfo registerServiceImpl(final IInterface service, final ComponentName component, final int userid) { + ManagedServiceInfo info = newServiceInfo(service, component, userid, + true /*isSystem*/, null /*connection*/, Build.VERSION_CODES.LOLLIPOP); + return registerServiceImpl(info); + } + + private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) { synchronized (mMutex) { try { - ManagedServiceInfo info = newServiceInfo(service, component, userid, - true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP); - service.asBinder().linkToDeath(info, 0); + info.service.asBinder().linkToDeath(info, 0); mServices.add(info); return info; } catch (RemoteException e) { @@ -728,7 +747,7 @@ abstract public class ManagedServices { */ private void unregisterServiceImpl(IInterface service, int userid) { ManagedServiceInfo info = removeServiceImpl(service, userid); - if (info != null && info.connection != null) { + if (info != null && info.connection != null && !info.isGuest(this)) { mContext.unbindService(info.connection); } } @@ -780,6 +799,10 @@ abstract public class ManagedServices { this.targetSdkVersion = targetSdkVersion; } + public boolean isGuest(ManagedServices host) { + return ManagedServices.this != host; + } + @Override public String toString() { return new StringBuilder("ManagedServiceInfo[") diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 49aa73efc3b2..86570496e095 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -33,12 +33,14 @@ import static android.service.notification.NotificationListenerService.HINT_HOST import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS; import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK; +import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON; import static android.service.notification.NotificationListenerService.TRIM_FULL; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; +import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AppGlobals; @@ -96,6 +98,7 @@ import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; +import android.service.notification.NotificationAssistantService; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.StatusBarNotification; @@ -154,6 +157,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** {@hide} */ public class NotificationManagerService extends SystemService { @@ -167,11 +171,13 @@ public class NotificationManagerService extends SystemService { // message codes static final int MESSAGE_TIMEOUT = 2; static final int MESSAGE_SAVE_POLICY_FILE = 3; - static final int MESSAGE_RECONSIDER_RANKING = 4; - static final int MESSAGE_RANKING_CONFIG_CHANGE = 5; - static final int MESSAGE_SEND_RANKING_UPDATE = 6; - static final int MESSAGE_LISTENER_HINTS_CHANGED = 7; - static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8; + static final int MESSAGE_SEND_RANKING_UPDATE = 4; + static final int MESSAGE_LISTENER_HINTS_CHANGED = 5; + static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6; + + // ranking thread messages + private static final int MESSAGE_RECONSIDER_RANKING = 1000; + private static final int MESSAGE_RANKING_SORT = 1001; static final int LONG_DELAY = 3500; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds @@ -281,11 +287,13 @@ public class NotificationManagerService extends SystemService { private final UserProfiles mUserProfiles = new UserProfiles(); private NotificationListeners mListeners; + private NotificationAssistant mAssistant; private ConditionProviders mConditionProviders; private NotificationUsageStats mUsageStats; private static final int MY_UID = Process.myUid(); private static final int MY_PID = Process.myPid(); + private RankingHandler mRankingHandler; private static class Archive { final int mBufferSize; @@ -738,6 +746,7 @@ public class NotificationManagerService extends SystemService { } } mListeners.onPackagesChanged(queryReplace, pkgList); + mAssistant.onPackagesChanged(queryReplace, pkgList); mConditionProviders.onPackagesChanged(queryReplace, pkgList); mRankingHelper.onPackagesChanged(queryReplace, pkgList); } @@ -779,6 +788,7 @@ public class NotificationManagerService extends SystemService { // Refresh managed services mConditionProviders.onUserSwitched(user); mListeners.onUserSwitched(user); + mAssistant.onUserSwitched(user); mZenModeHelper.onUserSwitched(user); } else if (action.equals(Intent.ACTION_USER_ADDED)) { mUserProfiles.updateCache(context); @@ -874,8 +884,9 @@ public class NotificationManagerService extends SystemService { extractorNames = new String[0]; } mUsageStats = new NotificationUsageStats(getContext()); + mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper()); mRankingHelper = new RankingHelper(getContext(), - new RankingWorkerHandler(mRankingThread.getLooper()), + mRankingHandler, mUsageStats, extractorNames); mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles); @@ -909,6 +920,7 @@ public class NotificationManagerService extends SystemService { importOldBlockDb(); mListeners = new NotificationListeners(); + mAssistant = new NotificationAssistant(); mStatusBar = getLocalService(StatusBarManagerInternal.class); mStatusBar.setNotificationDelegate(mNotificationDelegate); @@ -1025,6 +1037,7 @@ public class NotificationManagerService extends SystemService { // bind to listener services. mSettingsObserver.observe(); mListeners.onBootPhaseAppsCanStart(); + mAssistant.onBootPhaseAppsCanStart(); mConditionProviders.onBootPhaseAppsCanStart(); } } @@ -1896,6 +1909,22 @@ public class NotificationManagerService extends SystemService { Binder.restoreCallingIdentity(identity); } } + + @Override + public void setImportanceFromAssistant(INotificationListener token, String key, + int importance, CharSequence explanation) { + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mNotificationList) { + mAssistant.checkServiceTokenLocked(token); + NotificationRecord n = mNotificationsByKey.get(key); + n.setImportance(importance, explanation); + mRankingHandler.requestSort(); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } }; private String disableNotificationEffects(NotificationRecord record) { @@ -2037,6 +2066,8 @@ public class NotificationManagerService extends SystemService { pw.print(listener.component); } pw.println(')'); + pw.println("\n Notification assistant:"); + mAssistant.dump(pw, filter); } pw.println("\n Policy access:"); pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess); @@ -2533,9 +2564,14 @@ public class NotificationManagerService extends SystemService { updateLightsLocked(); } if (buzz || beep || blink) { - EventLogTags.writeNotificationAlert(record.getKey(), - buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); - mHandler.post(mBuzzBeepBlinked); + if (((record.getSuppressedVisualEffects() + & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0)) { + if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on"); + } else { + EventLogTags.writeNotificationAlert(record.getKey(), + buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0); + mHandler.post(mBuzzBeepBlinked); + } } } @@ -2686,7 +2722,7 @@ public class NotificationManagerService extends SystemService { } } - private void handleRankingConfigChange() { + private void handleRankingSort() { synchronized (mNotificationList) { final int N = mNotificationList.size(); ArrayList<String> orderBefore = new ArrayList<String>(N); @@ -2714,7 +2750,8 @@ public class NotificationManagerService extends SystemService { record.setIntercepted(mZenModeHelper.shouldIntercept(record)); if (record.isIntercepted()) { int suppressed = (mZenModeHelper.shouldSuppressLight() ? SUPPRESSED_EFFECT_LIGHTS : 0) - | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0); + | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0) + | (mZenModeHelper.shouldSuppressScreenOn() ? SUPPRESSED_EFFECT_SCREEN_ON : 0); record.setSuppressedVisualEffects(suppressed); } } @@ -2788,9 +2825,9 @@ public class NotificationManagerService extends SystemService { } - private final class RankingWorkerHandler extends Handler + private final class RankingHandlerWorker extends Handler implements RankingHandler { - public RankingWorkerHandler(Looper looper) { + public RankingHandlerWorker(Looper looper) { super(looper); } @@ -2800,11 +2837,23 @@ public class NotificationManagerService extends SystemService { case MESSAGE_RECONSIDER_RANKING: handleRankingReconsideration(msg); break; - case MESSAGE_RANKING_CONFIG_CHANGE: - handleRankingConfigChange(); + case MESSAGE_RANKING_SORT: + handleRankingSort(); break; } } + + public void requestSort() { + removeMessages(MESSAGE_RANKING_SORT); + sendEmptyMessage(MESSAGE_RANKING_SORT); + } + + public void requestReconsideration(RankingReconsideration recon) { + Message m = Message.obtain(this, + NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon); + long delay = recon.getDelay(TimeUnit.MILLISECONDS); + sendMessageDelayed(m, delay); + } } // Notifications @@ -3305,6 +3354,45 @@ public class NotificationManagerService extends SystemService { return true; } + public class NotificationAssistant extends ManagedServices { + + public NotificationAssistant() { + super(getContext(), mHandler, mNotificationList, mUserProfiles); + } + + @Override + protected Config getConfig() { + Config c = new Config(); + c.caption = "notification assistant"; + c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE; + c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT; + c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE; + c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS; + c.clientLabel = R.string.notification_assistant_binding_label; + return c; + } + + @Override + protected IInterface asInterface(IBinder binder) { + return INotificationListener.Stub.asInterface(binder); + } + + @Override + protected boolean checkType(IInterface service) { + return service instanceof INotificationListener; + } + + @Override + protected void onServiceAdded(ManagedServiceInfo info) { + mListeners.registerGuestService(info); + } + + @Override + protected void onServiceRemovedLocked(ManagedServiceInfo removed) { + mListeners.unregisterService(removed.service, removed.userid); + } + } + public class NotificationListeners extends ManagedServices { private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); @@ -3332,6 +3420,11 @@ public class NotificationManagerService extends SystemService { } @Override + protected boolean checkType(IInterface service) { + return service instanceof INotificationListener; + } + + @Override public void onServiceAdded(ManagedServiceInfo info) { final INotificationListener listener = (INotificationListener) info.service; final NotificationRankingUpdate update; @@ -3366,7 +3459,6 @@ public class NotificationManagerService extends SystemService { public int getOnNotificationPostedTrim(ManagedServiceInfo info) { return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL; - } /** diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/services/core/java/com/android/server/notification/RankingHandler.java index 805fe2c67695..80bb4f089639 100644 --- a/core/java/android/service/notification/NotificationAdjustment.aidl +++ b/services/core/java/com/android/server/notification/RankingHandler.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.server.notification; -package android.service.notification; - -parcelable NotificationAdjustment;
\ No newline at end of file +public interface RankingHandler { + public void requestSort(); + public void requestReconsideration(RankingReconsideration recon); +} diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 3287f67a6b86..0662e7a68cea 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -19,10 +19,7 @@ import android.app.Notification; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Handler; -import android.os.Message; import android.os.UserHandle; -import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; import android.text.TextUtils; import android.util.ArrayMap; @@ -40,7 +37,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; public class RankingHelper implements RankingConfig { private static final String TAG = "RankingHelper"; @@ -73,10 +69,10 @@ public class RankingHelper implements RankingConfig { private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record private final Context mContext; - private final Handler mRankingHandler; + private final RankingHandler mRankingHandler; - public RankingHelper(Context context, Handler rankingHandler, NotificationUsageStats usageStats, - String[] extractorNames) { + public RankingHelper(Context context, RankingHandler rankingHandler, + NotificationUsageStats usageStats, String[] extractorNames) { mContext = context; mRankingHandler = rankingHandler; @@ -119,10 +115,7 @@ public class RankingHelper implements RankingConfig { try { RankingReconsideration recon = extractor.process(r); if (recon != null) { - Message m = Message.obtain(mRankingHandler, - NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon); - long delay = recon.getDelay(TimeUnit.MILLISECONDS); - mRankingHandler.sendMessageDelayed(m, delay); + mRankingHandler.requestReconsideration(recon); } } catch (Throwable t) { Slog.w(TAG, "NotificationSignalExtractor failed.", t); @@ -287,7 +280,7 @@ public class RankingHelper implements RankingConfig { for (int i = 0; i < N; i++) { mSignalExtractors[i].setConfig(this); } - mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE); + mRankingHandler.requestSort(); } public void sort(ArrayList<NotificationRecord> notificationList) { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 85c3cf88af19..276c6babce24 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -155,6 +155,12 @@ public class ZenModeHelper { } } + public boolean shouldSuppressScreenOn() { + synchronized (mConfig) { + return !mConfig.allowScreenOn; + } + } + public void addCallback(Callback callback) { mCallbacks.add(callback); } @@ -435,11 +441,12 @@ public class ZenModeHelper { return; } pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s," - + "events=%s,reminders=%s,lights=%s,peek=%s)\n", + + "events=%s,reminders=%s,lights=%s,peek=%s,screenOn=%s)\n", config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom), config.allowRepeatCallers, config.allowMessages, ZenModeConfig.sourceToString(config.allowMessagesFrom), - config.allowEvents, config.allowReminders, config.allowLights, config.allowPeek); + config.allowEvents, config.allowReminders, config.allowLights, config.allowPeek, + config.allowScreenOn); pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule); if (config.automaticRules.isEmpty()) return; final int N = config.automaticRules.size(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7bb6d1d91019..7e186a0da0ee 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -106,6 +106,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.AppsQueryHelper; +import android.content.pm.ComponentInfo; import android.content.pm.EphemeralApplicationInfo; import android.content.pm.EphemeralResolveInfo; import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo; @@ -122,7 +123,6 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; -import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -207,7 +207,6 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; @@ -308,7 +307,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_DEXOPT = false; private static final boolean DEBUG_ABI_SELECTION = false; private static final boolean DEBUG_EPHEMERAL = false; - private static final boolean DEBUG_ENCRYPTION_AWARE = false; + private static final boolean DEBUG_TRIAGED_MISSING = false; static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false; @@ -2848,6 +2847,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + flags = updateFlagsForPackage(flags, userId, packageName); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package info"); // reader synchronized (mPackages) { @@ -2898,6 +2898,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public int getPackageUidEtc(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; + flags = updateFlagsForPackage(flags, userId, packageName); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid"); // reader @@ -2924,10 +2925,8 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public int[] getPackageGidsEtc(String packageName, int flags, int userId) { - if (!sUserManager.exists(userId)) { - return null; - } - + if (!sUserManager.exists(userId)) return null; + flags = updateFlagsForPackage(flags, userId, packageName); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "getPackageGids"); @@ -2949,8 +2948,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } - static PermissionInfo generatePermissionInfo( - BasePermission bp, int flags) { + static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) { if (bp.perm != null) { return PackageParser.generatePermissionInfo(bp.perm, flags); } @@ -3068,6 +3066,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; + flags = updateFlagsForApplication(flags, userId, packageName); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info"); // writer synchronized (mPackages) { @@ -3182,46 +3181,88 @@ public class PackageManagerService extends IPackageManager.Stub { } /** - * Augment the given flags depending on current user running state. This is - * purposefully done before acquiring {@link #mPackages} lock. + * Update given flags based on encryption status of current user. */ - private int augmentFlagsForUser(int flags, int userId, Object cookie) { - if (cookie instanceof Intent) { - // If intent claims to be triaged, then we're fine with default - // matching behavior below - final Intent intent = (Intent) cookie; - if ((intent.getFlags() & Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED) != 0) { - flags |= PackageManager.MATCH_ENCRYPTION_DEFAULT; - } - } - - if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY - | PackageManager.MATCH_ENCRYPTION_AWARE_ONLY)) != 0) { - // Caller expressed an opinion about what components they want to - // see, so fall through and give them what they want + private int updateFlagsForEncryption(int flags, int userId) { + if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE + | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) { + // Caller expressed an explicit opinion about what encryption + // aware/unaware components they want to see, so fall through and + // give them what they want } else { // Caller expressed no opinion, so match based on user state if (isUserKeyUnlocked(userId)) { flags |= PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE; } else { - flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY; - - // If we have a system caller that hasn't done their homework to - // decide they want this default behavior, yell at them - if (DEBUG_ENCRYPTION_AWARE && (Binder.getCallingUid() == Process.SYSTEM_UID) - && ((flags & PackageManager.MATCH_ENCRYPTION_DEFAULT) == 0)) { - Log.v(TAG, "Caller hasn't been triaged for FBE; they asked about " + cookie, - new Throwable()); - } + flags |= PackageManager.MATCH_ENCRYPTION_AWARE; } } return flags; } + /** + * Update given flags when being used to request {@link PackageInfo}. + */ + private int updateFlagsForPackage(int flags, int userId, Object cookie) { + boolean triaged = true; + if ((flags & PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS + | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS) != 0) { + // Caller is asking for component details, so they'd better be + // asking for specific encryption matching behavior, or be triaged + if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE + | PackageManager.MATCH_ENCRYPTION_AWARE + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) { + triaged = false; + } + } + if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) { + triaged = false; + } + if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) { + Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie, + new Throwable()); + } + return updateFlagsForEncryption(flags, userId); + } + + /** + * Update given flags when being used to request {@link ApplicationInfo}. + */ + private int updateFlagsForApplication(int flags, int userId, Object cookie) { + return updateFlagsForPackage(flags, userId, cookie); + } + + /** + * Update given flags when being used to request {@link ComponentInfo}. + */ + private int updateFlagsForComponent(int flags, int userId, Object cookie) { + boolean triaged = true; + // Caller is asking for component details, so they'd better be + // asking for specific encryption matching behavior, or be triaged + if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE + | PackageManager.MATCH_ENCRYPTION_AWARE + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) { + triaged = false; + } + if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) { + Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie, + new Throwable()); + } + return updateFlagsForEncryption(flags, userId); + } + + /** + * Update given flags when being used to request {@link ResolveInfo}. + */ + private int updateFlagsForResolve(int flags, int userId, Object cookie) { + return updateFlagsForComponent(flags, userId, cookie); + } + @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, component); + flags = updateFlagsForComponent(flags, userId, component); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info"); synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -3266,7 +3307,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, component); + flags = updateFlagsForComponent(flags, userId, component); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mReceivers.mActivities.get(component); @@ -3285,7 +3326,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, component); + flags = updateFlagsForComponent(flags, userId, component); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get service info"); synchronized (mPackages) { PackageParser.Service s = mServices.mServices.get(component); @@ -3304,7 +3345,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, component); + flags = updateFlagsForComponent(flags, userId, component); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mProviders.mProviders.get(component); @@ -4429,7 +4470,7 @@ public class PackageManagerService extends IPackageManager.Stub { public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent"); List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId); final ResolveInfo bestChoice = @@ -4687,7 +4728,7 @@ public class PackageManagerService extends IPackageManager.Stub { List<ResolveInfo> query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); // writer synchronized (mPackages) { if (intent.getSelector() != null) { @@ -4886,7 +4927,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities"); ComponentName comp = intent.getComponent(); if (comp == null) { @@ -5370,7 +5411,7 @@ public class PackageManagerService extends IPackageManager.Stub { Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activity options"); final String resultsAction = intent.getAction(); @@ -5543,7 +5584,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -5580,7 +5621,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId); if (query != null) { if (query.size() >= 1) { @@ -5596,7 +5637,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -5634,7 +5675,7 @@ public class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentContentProviders( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - flags = augmentFlagsForUser(flags, userId, intent); + flags = updateFlagsForResolve(flags, userId, intent); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -5670,8 +5711,9 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { + if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); + flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; - enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "get installed packages"); // writer @@ -5750,7 +5792,8 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { - if (!sUserManager.exists(userId)) return null; + if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); + flags = updateFlagsForPackage(flags, userId, permissions); final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; // writer @@ -5777,7 +5820,8 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) { - if (!sUserManager.exists(userId)) return null; + if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); + flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; // writer @@ -5920,7 +5964,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, name); + flags = updateFlagsForComponent(flags, userId, name); // reader synchronized (mPackages) { final PackageParser.Provider provider = mProvidersByAuthority.get(name); @@ -5972,7 +6016,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int userId = processName != null ? UserHandle.getUserId(uid) : UserHandle.getCallingUserId(); if (!sUserManager.exists(userId)) return null; - flags = augmentFlagsForUser(flags, userId, processName); + flags = updateFlagsForComponent(flags, userId, processName); ArrayList<ProviderInfo> finalList = null; // reader @@ -6009,8 +6053,7 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public InstrumentationInfo getInstrumentationInfo(ComponentName name, - int flags) { + public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) { // reader synchronized (mPackages) { final PackageParser.Instrumentation i = mInstrumentation.get(name); @@ -6205,7 +6248,6 @@ public class PackageManagerService extends IPackageManager.Stub { try { pp.collectCertificates(pkg, parseFlags); - pp.collectManifestDigest(pkg); } catch (PackageParserException e) { throw PackageManagerException.from(e); } @@ -10037,7 +10079,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final VerificationParams verifParams = new VerificationParams( null, sessionParams.originatingUri, sessionParams.referrerUri, - sessionParams.originatingUid, null); + sessionParams.originatingUid); verifParams.setInstallerUid(installerUid); final OriginInfo origin; @@ -11019,13 +11061,6 @@ public class PackageManagerService extends IPackageManager.Stub { + " file=" + origin.file + " cid=" + origin.cid + "}"; } - public ManifestDigest getManifestDigest() { - if (verificationParams == null) { - return null; - } - return verificationParams.getManifestDigest(); - } - private int installLocationPolicy(PackageInfoLite pkgLite) { String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; @@ -11444,7 +11479,6 @@ public class PackageManagerService extends IPackageManager.Stub { final int installFlags; final String installerPackageName; final String volumeUuid; - final ManifestDigest manifestDigest; final UserHandle user; final String abiOverride; final String[] installGrantPermissions; @@ -11459,7 +11493,7 @@ public class PackageManagerService extends IPackageManager.Stub { InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, String volumeUuid, - ManifestDigest manifestDigest, UserHandle user, String[] instructionSets, + UserHandle user, String[] instructionSets, String abiOverride, String[] installGrantPermissions, String traceMethod, int traceCookie) { this.origin = origin; @@ -11468,7 +11502,6 @@ public class PackageManagerService extends IPackageManager.Stub { this.observer = observer; this.installerPackageName = installerPackageName; this.volumeUuid = volumeUuid; - this.manifestDigest = manifestDigest; this.user = user; this.instructionSets = instructionSets; this.abiOverride = abiOverride; @@ -11570,7 +11603,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ FileInstallArgs(InstallParams params) { super(params.origin, params.move, params.observer, params.installFlags, - params.installerPackageName, params.volumeUuid, params.getManifestDigest(), + params.installerPackageName, params.volumeUuid, params.getUser(), null /* instruction sets */, params.packageAbiOverride, params.grantedRuntimePermissions, params.traceMethod, params.traceCookie); @@ -11581,7 +11614,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** Existing install */ FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { - super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets, + super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets, null, null, null, 0); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; @@ -11808,7 +11841,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ AsecInstallArgs(InstallParams params) { super(params.origin, params.move, params.observer, params.installFlags, - params.installerPackageName, params.volumeUuid, params.getManifestDigest(), + params.installerPackageName, params.volumeUuid, params.getUser(), null /* instruction sets */, params.packageAbiOverride, params.grantedRuntimePermissions, params.traceMethod, params.traceCookie); @@ -11818,7 +11851,7 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String fullCodePath, String[] instructionSets, boolean isExternal, boolean isForwardLocked) { super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0) - | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null, + | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSets, null, null, null, 0); // Hackily pretend we're still looking at a full code path if (!fullCodePath.endsWith(RES_FILE_NAME)) { @@ -11835,7 +11868,7 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) { super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) - | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null, + | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, instructionSets, null, null, null, 0); this.cid = cid; setMountPath(PackageHelper.getSdDir(cid)); @@ -12102,7 +12135,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ MoveInstallArgs(InstallParams params) { super(params.origin, params.move, params.observer, params.installFlags, - params.installerPackageName, params.volumeUuid, params.getManifestDigest(), + params.installerPackageName, params.volumeUuid, params.getUser(), null /* instruction sets */, params.packageAbiOverride, params.grantedRuntimePermissions, params.traceMethod, params.traceCookie); @@ -12876,35 +12909,6 @@ public class PackageManagerService extends IPackageManager.Stub { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - /* If the installer passed in a manifest digest, compare it now. */ - if (args.manifestDigest != null) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectManifestDigest"); - try { - pp.collectManifestDigest(pkg); - } catch (PackageParserException e) { - res.setError("Failed collect during installPackageLI", e); - return; - } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - if (DEBUG_INSTALL) { - final String parsedManifest = pkg.manifestDigest == null ? "null" - : pkg.manifestDigest.toString(); - Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. " - + parsedManifest); - } - - if (!args.manifestDigest.equals(pkg.manifestDigest)) { - res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed"); - return; - } - } else if (DEBUG_INSTALL) { - final String parsedManifest = pkg.manifestDigest == null - ? "null" : pkg.manifestDigest.toString(); - Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest); - } - // Get rid of all references to package scan path via parser. pp = null; String oldCodePath = null; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index d2a3ede9163f..b33013995350 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3846,9 +3846,9 @@ final class Settings { } } - final boolean matchesUnaware = ((flags & PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY) != 0) + final boolean matchesUnaware = ((flags & PackageManager.MATCH_ENCRYPTION_UNAWARE) != 0) && !componentInfo.encryptionAware; - final boolean matchesAware = ((flags & PackageManager.MATCH_ENCRYPTION_AWARE_ONLY) != 0) + final boolean matchesAware = ((flags & PackageManager.MATCH_ENCRYPTION_AWARE) != 0) && componentInfo.encryptionAware; return matchesUnaware || matchesAware; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6fcf1d6912f5..72611b7703e9 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3381,7 +3381,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (awakenFromDreams) { awakenDreams(); } - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); hideRecentApps(false, true); } else { // Otherwise, just launch Home @@ -4243,6 +4242,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 && (attrs.type == TYPE_STATUS_BAR || attrs.type == TYPE_TOAST + || attrs.type == TYPE_DOCK_DIVIDER || attrs.type == TYPE_VOICE_INTERACTION_STARTING || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) { diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 29e3c6328af4..549d2dc39468 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -126,7 +126,7 @@ public class KeyguardServiceDelegate { final ComponentName keyguardComponent = ComponentName.unflattenFromString( resources.getString(com.android.internal.R.string.config_keyguardComponent)); - intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED); + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); intent.setComponent(keyguardComponent); if (!context.bindServiceAsUser(intent, mKeyguardConnection, diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index b49641fb6ab6..573aaec034cf 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.StackId; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -30,6 +31,7 @@ import com.android.server.wm.WindowManagerService.H; import android.annotation.NonNull; import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.os.Message; import android.os.RemoteException; import android.util.Slog; @@ -38,6 +40,7 @@ import android.view.View; import android.view.WindowManager; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.ArrayList; class AppTokenList extends ArrayList<AppWindowToken> { @@ -123,6 +126,10 @@ class AppWindowToken extends WindowToken { // True if the windows associated with this token should be cropped to their stack bounds. boolean mCropWindowsToStack; + boolean mAlwaysFocusable; + + ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); + AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { super(_service, _token.asBinder(), @@ -256,8 +263,8 @@ class AppWindowToken extends WindowToken { return candidate; } - boolean stackCanReceiveKeys() { - return (windows.size() > 0) ? windows.get(windows.size() - 1).stackCanReceiveKeys() : false; + boolean windowsAreFocusable() { + return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable; } boolean isVisible() { @@ -434,6 +441,23 @@ class AppWindowToken extends WindowToken { } } + /** + * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds + * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even + * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen + * with a queue. + */ + void freezeBounds() { + mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds)); + } + + /** + * Unfreezes the previously frozen bounds. See {@link #freezeBounds}. + */ + void unfreezeBounds() { + mFrozenBounds.remove(); + } + @Override void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); @@ -480,6 +504,9 @@ class AppWindowToken extends WindowToken { pw.print(" startingDisplayed="); pw.print(startingDisplayed); pw.print(" startingMoved"); pw.println(startingMoved); } + if (!mFrozenBounds.isEmpty()) { + pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds); + } } @Override diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 8f3d3e30ad1a..72953183a173 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -18,11 +18,17 @@ package com.android.server.wm; import android.content.Context; import android.graphics.Rect; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Slog; -import android.view.IDockDividerVisibilityListener; +import android.view.DisplayInfo; +import android.view.IDockedStackListener; +import android.view.SurfaceControl; + +import com.android.server.wm.DimLayer.DimLayerUser; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; @@ -33,7 +39,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; /** * Keeps information about the docked stack divider. */ -public class DockedStackDividerController { +public class DockedStackDividerController implements DimLayerUser { private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM; @@ -44,9 +50,10 @@ public class DockedStackDividerController { private WindowState mWindow; private final Rect mTmpRect = new Rect(); private final Rect mLastRect = new Rect(); - private IDockDividerVisibilityListener mListener; private boolean mLastVisibility = false; - private boolean mForceVisibilityReevaluation; + private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners + = new RemoteCallbackList<>(); + private final DimLayer mDimLayer; DockedStackDividerController(Context context, DisplayContent displayContent) { mDisplayContent = displayContent; @@ -54,6 +61,7 @@ public class DockedStackDividerController { com.android.internal.R.dimen.docked_stack_divider_thickness); mDividerInsets = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_insets); + mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId()); } boolean isResizing() { @@ -83,15 +91,16 @@ public class DockedStackDividerController { return; } mLastVisibility = visible; - if (mListener != null) { - try { - mListener.onDockDividerVisibilityChanged(visible); - } catch (RemoteException e) { - Slog.e(TAG, "visibility call failed: " + e); - } + notifyDockedDividerVisibilityChanged(visible); + if (!visible) { + setResizeDimLayer(false, INVALID_STACK_ID, 0f); } } + boolean wasVisible() { + return mLastVisibility; + } + void positionDockedStackedDivider(Rect frame) { TaskStack stack = mDisplayContent.getDockedStackLocked(); if (stack == null) { @@ -127,11 +136,76 @@ public class DockedStackDividerController { mLastRect.set(frame); } - public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) { - if (mListener != null && listener != null) { - throw new IllegalStateException("Dock divider visibility listener already set!"); + void notifyDockedDividerVisibilityChanged(boolean visible) { + final int size = mDockedStackListeners.beginBroadcast(); + for (int i = 0; i < size; ++i) { + final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); + try { + listener.onDividerVisibilityChanged(visible); + } catch (RemoteException e) { + Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e); + } + } + mDockedStackListeners.finishBroadcast(); + } + + void notifyDockedStackExistsChanged(boolean exists) { + final int size = mDockedStackListeners.beginBroadcast(); + for (int i = 0; i < size; ++i) { + final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); + try { + listener.onDockedStackExistsChanged(exists); + } catch (RemoteException e) { + Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e); + } } - mListener = listener; - reevaluateVisibility(true); + mDockedStackListeners.finishBroadcast(); + } + + void registerDockedStackListener(IDockedStackListener listener) { + mDockedStackListeners.register(listener); + notifyDockedDividerVisibilityChanged(wasVisible()); + notifyDockedStackExistsChanged( + mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null); + } + + void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + SurfaceControl.openTransaction(); + TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId); + boolean visibleAndValid = visible && stack != null; + if (visibleAndValid) { + stack.getDimBounds(mTmpRect); + if (mTmpRect.height() > 0 && mTmpRect.width() > 0) { + mDimLayer.setBounds(mTmpRect); + mDimLayer.show(mDisplayContent.mService.mLayersController.getResizeDimLayer(), + alpha, 0 /* duration */); + } else { + visibleAndValid = false; + } + } + if (!visibleAndValid) { + mDimLayer.hide(); + } + SurfaceControl.closeTransaction(); + } + + @Override + public boolean isFullscreen() { + return false; + } + + @Override + public DisplayInfo getDisplayInfo() { + return mDisplayContent.getDisplayInfo(); + } + + @Override + public void getDimBounds(Rect outBounds) { + // This dim layer user doesn't need this. + } + + @Override + public String toShortString() { + return TAG; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 27e7a31bd07b..223e03ad4cc4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -61,6 +61,10 @@ class Task implements DimLayer.DimLayerUser { // Content limits relative to the DisplayContent this sits in. private Rect mBounds = new Rect(); + final Rect mPreparedFrozenBounds = new Rect(); + + // Bounds used to calculate the insets. + private final Rect mTempInsetBounds = new Rect(); // Device rotation as of the last time {@link #mBounds} was set. int mRotation; @@ -197,8 +201,7 @@ class Task implements DimLayer.DimLayerUser { boolean removeAppToken(AppWindowToken wtoken) { boolean removed = mAppTokens.remove(wtoken); if (mAppTokens.size() == 0) { - EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, - "removeAppToken: last token"); + EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token"); if (mDeferRemoval) { removeLocked(); } @@ -267,6 +270,26 @@ class Task implements DimLayer.DimLayerUser { return boundsChange; } + /** + * Sets the bounds used to calculate the insets. See + * {@link android.app.IActivityManager#resizeDockedStack} why this is needed. + */ + void setTempInsetBounds(Rect tempInsetBounds) { + if (tempInsetBounds != null) { + mTempInsetBounds.set(tempInsetBounds); + } else { + mTempInsetBounds.setEmpty(); + } + } + + /** + * Gets the bounds used to calculate the insets. See + * {@link android.app.IActivityManager#resizeDockedStack} why this is needed. + */ + void getTempInsetBounds(Rect out) { + out.set(mTempInsetBounds); + } + void setResizeable(boolean resizeable) { mResizeable = resizeable; } @@ -291,6 +314,14 @@ class Task implements DimLayer.DimLayerUser { return true; } + /** + * Prepares the task bounds to be frozen with the current size. See + * {@link AppWindowToken#freezeBounds}. + */ + void prepareFreezingBounds() { + mPreparedFrozenBounds.set(mBounds); + } + boolean scrollLocked(Rect bounds) { // shift the task bound if it doesn't fully cover the stack area mStack.getDimBounds(mTmpRect); @@ -357,7 +388,6 @@ class Task implements DimLayer.DimLayerUser { mStack.getDisplayContent().getLogicalDisplayRect(out); } - /** * Calculate the maximum visible area of this task. If the task has only one app, * the result will be visible frame of that app. If the task has more than one apps, @@ -579,5 +609,6 @@ class Task implements DimLayer.DimLayerUser { pw.print(prefix + prefix); pw.print("mBounds="); pw.println(mBounds.toShortString()); pw.print(prefix + prefix); pw.print("mdr="); pw.println(mDeferRemoval); pw.print(prefix + prefix); pw.print("appTokens="); pw.println(mAppTokens); + pw.print(prefix + prefix); pw.print("mTempInsetBounds="); pw.println(mTempInsetBounds); } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index b9618791b848..67debe6e3773 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -114,7 +114,8 @@ public class TaskStack implements DimLayer.DimLayerUser { * @return True if the stack bounds was changed. * */ boolean setBounds( - Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) { + Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { if (!setBounds(stackBounds)) { return false; } @@ -136,6 +137,9 @@ public class TaskStack implements DimLayer.DimLayerUser { task.scrollLocked(mTmpRect); } else { task.setBounds(bounds, config); + task.setTempInsetBounds( + taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) + : null); } } else { Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); @@ -144,6 +148,13 @@ public class TaskStack implements DimLayer.DimLayerUser { return true; } + void prepareFreezingTaskBounds() { + for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { + final Task task = mTasks.get(taskNdx); + task.prepareFreezingBounds(); + } + } + boolean isFullscreenBounds(Rect bounds) { if (mDisplayContent == null || bounds == null) { return true; diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java index 263b411097d8..2cf26180441f 100644 --- a/services/core/java/com/android/server/wm/WindowLayersController.java +++ b/services/core/java/com/android/server/wm/WindowLayersController.java @@ -1,9 +1,20 @@ -package com.android.server.wm; +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER; +package com.android.server.wm; import android.app.ActivityManager.StackId; import android.util.Slog; @@ -11,6 +22,11 @@ import android.view.Display; import java.io.PrintWriter; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER; + /** * Controller for assigning layers to windows on the display. * @@ -42,7 +58,6 @@ public class WindowLayersController { private WindowState mPinnedWindow = null; private WindowState mDockedWindow = null; private WindowState mDockDivider = null; - private WindowState mImeWindow = null; private WindowState mReplacingWindow = null; final void assignLayersLocked(WindowList windows) { @@ -133,6 +148,14 @@ public class WindowLayersController { return 0; } + /** + * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just + * above all application surfaces. + */ + int getResizeDimLayer() { + return mDockDivider.mLayer - 1; + } + private void logDebugLayers(WindowList windows) { for (int i = 0, n = windows.size(); i < n; i++) { final WindowState w = windows.get(i); @@ -146,16 +169,13 @@ public class WindowLayersController { private void clear() { mHighestApplicationLayer = 0; - mImeWindow = null; mPinnedWindow = null; mDockedWindow = null; mDockDivider = null; } private void collectSpecialWindows(WindowState w) { - if (w.mIsImWindow) { - mImeWindow = w; - } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER) { + if (w.mAttrs.type == TYPE_DOCK_DIVIDER) { mDockDivider = w; } else { final TaskStack stack = w.getStack(); @@ -175,18 +195,17 @@ public class WindowLayersController { // For pinned and docked stack window, we want to make them above other windows // also when these windows are animating. layer = assignAndIncreaseLayerIfNeeded(mDockedWindow, layer); + + // Leave some space here so the dim layer while dismissing docked/fullscreen stack has space + // below the divider but above the app windows. It needs to be below the divider in because + // the divider sometimes overlaps the app windows. + layer++; layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer); // We know that we will be animating a relaunching window in the near future, // which will receive a z-order increase. We want the replaced window to // immediately receive the same treatment, e.g. to be above the dock divider. layer = assignAndIncreaseLayerIfNeeded(mReplacingWindow, layer); layer = assignAndIncreaseLayerIfNeeded(mPinnedWindow, layer); - final WindowState inputMethodTarget = mService.mInputMethodTarget; - // There might be no applications windows yet, so we need to make sure we uplift the input - // method above the target. - layer = Math.max(layer, - inputMethodTarget != null ? inputMethodTarget.mWinAnimator.mAnimLayer + 1 : 0); - layer = assignAndIncreaseLayerIfNeeded(mImeWindow, layer); } private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6385caac3fa9..d722ec7a4803 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -16,81 +16,11 @@ package com.android.server.wm; -import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.StatusBarManager.DISABLE_MASK; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_SECURE; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; -import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; -import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; -import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; -import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; -import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END; -import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; -import static com.android.server.wm.WindowManagerDebugConfig.HIDE_STACK_CRAWLS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - import android.Manifest; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager.StackId; import android.app.ActivityManagerNative; import android.app.AppOpsManager; import android.app.IActivityManager; @@ -127,6 +57,7 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -154,7 +85,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; -import android.view.IDockDividerVisibilityListener; +import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -224,9 +155,83 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; +import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; +import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.StatusBarManager.DISABLE_MASK; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.WindowManager.DOCKED_INVALID; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_DREAM; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; +import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END; +import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; +import static com.android.server.wm.WindowManagerDebugConfig.HIDE_STACK_CRAWLS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { + private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM; + static final int LAYOUT_REPEAT_THRESHOLD = 4; static final boolean PROFILE_ORIENTATION = false; @@ -2426,13 +2431,17 @@ public class WindowManagerService extends IWindowManager.Stub } } - void setInsetsWindow(Session session, IWindow client, - int touchableInsets, Rect contentInsets, + void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableRegion) { long origId = Binder.clearCallingIdentity(); try { synchronized (mWindowMap) { WindowState w = windowForClientLocked(session, client, false); + if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w + + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets + + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets + + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion + + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets); if (w != null) { w.mGivenInsetsPending = false; w.mGivenContentInsets.set(contentInsets); @@ -3203,7 +3212,8 @@ public class WindowManagerService extends IWindowManager.Stub public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, - Rect taskBounds, Configuration config, boolean cropWindowsToStack) { + Rect taskBounds, Configuration config, boolean cropWindowsToStack, + boolean alwaysFocusable) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3238,6 +3248,7 @@ public class WindowManagerService extends IWindowManager.Stub (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; atoken.mLaunchTaskBehind = launchTaskBehind; atoken.mCropWindowsToStack = cropWindowsToStack; + atoken.mAlwaysFocusable = alwaysFocusable; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); @@ -4651,6 +4662,10 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_STACK) Slog.d(TAG_WM, "attachStack: stackId=" + stackId); stack = new TaskStack(this, stackId); mStackIdToStack.put(stackId, stack); + if (stackId == DOCKED_STACK_ID) { + getDefaultDisplayContentLocked().mDividerControllerLocked + .notifyDockedStackExistsChanged(true); + } } stack.attachDisplayContent(displayContent); displayContent.attachStack(stack, onTop); @@ -4676,6 +4691,10 @@ public class WindowManagerService extends IWindowManager.Stub void detachStackLocked(DisplayContent displayContent, TaskStack stack) { displayContent.detachStack(stack); stack.detachDisplay(); + if (stack.mStackId == DOCKED_STACK_ID) { + getDefaultDisplayContentLocked().mDividerControllerLocked + .notifyDockedStackExistsChanged(false); + } } public void detachStack(int stackId) { @@ -4822,14 +4841,16 @@ public class WindowManagerService extends IWindowManager.Stub * @return True if the stack is now fullscreen. * */ public boolean resizeStack(int stackId, Rect bounds, - SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) { + SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { synchronized (mWindowMap) { final TaskStack stack = mStackIdToStack.get(stackId); if (stack == null) { throw new IllegalArgumentException("resizeStack: stackId " + stackId + " not found."); } - if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) { + if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds) + && stack.isVisibleLocked()) { stack.resizeWindows(); stack.getDisplayContent().layoutNeeded = true; mWindowPlacerLocked.performSurfacePlacement(); @@ -4838,6 +4859,17 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void prepareFreezingTaskBounds(int stackId) { + synchronized (mWindowMap) { + final TaskStack stack = mStackIdToStack.get(stackId); + if (stack == null) { + throw new IllegalArgumentException("prepareFreezingTaskBounds: stackId " + stackId + + " not found."); + } + stack.prepareFreezingTaskBounds(); + } + } + public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds, Configuration config) { synchronized (mWindowMap) { @@ -9078,8 +9110,8 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken == token) { break; } - if (mFocusedApp == token && token.stackCanReceiveKeys()) { - // Whoops, we are below the focused app whose stack can receive keys... + if (mFocusedApp == token && token.windowsAreFocusable()) { + // Whoops, we are below the focused app whose windows are focusable... // No focus for you!!! if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Reached focused app=" + mFocusedApp); @@ -9464,6 +9496,32 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void notifyAppRelaunching(IBinder token) { + synchronized (mWindowMap) { + AppWindowToken appWindow = findAppWindowToken(token); + if (canFreezeBounds(appWindow)) { + appWindow.freezeBounds(); + } + } + } + + public void notifyAppRelaunchingFinished(IBinder token) { + synchronized (mWindowMap) { + AppWindowToken appWindow = findAppWindowToken(token); + if (canFreezeBounds(appWindow)) { + appWindow.unfreezeBounds(); + } + } + } + + private boolean canFreezeBounds(AppWindowToken appWindow) { + + // For freeform windows, we can't freeze the bounds at the moment because this would make + // the resizing unresponsive. + return appWindow != null && appWindow.mTask != null + && !appWindow.mTask.inFreeformWorkspace(); + } + void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) { pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)"); mPolicy.dump(" ", pw, args); @@ -10128,7 +10186,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { appWindowToken = findAppWindowToken(token); if (appWindowToken == null || !appWindowToken.isVisible()) { - Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token); + Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + + token); return; } appWindowToken.setReplacingWindows(animate); @@ -10157,6 +10216,14 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + synchronized (mWindowMap) { + getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer( + visible, targetStackId, alpha); + } + } + public void setTaskResizeable(int taskId, boolean resizeable) { synchronized (mWindowMap) { Task task = mTaskIdToTask.get(taskId); @@ -10175,26 +10242,14 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) { + public void registerDockedStackListener(IDockedStackListener listener) { if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS, - "registerDockDividerVisibilityListener()")) { + "registerDockedStackListener()")) { return; } // TODO(multi-display): The listener is registered on the default display only. - final DockedStackDividerController controller = - getDefaultDisplayContentLocked().getDockedDividerController(); - controller.registerDockDividerVisibilityListener(listener); - try { - listener.asBinder().linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - getDefaultDisplayContentLocked().getDockedDividerController() - .registerDockDividerVisibilityListener(null); - } - }, 0); - } catch (RemoteException e) { - controller.registerDockDividerVisibilityListener(null); - } + getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener( + listener); } private final class LocalService extends WindowManagerInternal { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 733bc2993d90..6e4e01f38212 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -309,6 +309,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { // possible to draw. final Rect mOutsetFrame = new Rect(); + /** + * Usually empty. Set to the task's tempInsetFrame. See + *{@link android.app.IActivityManager#resizeDockedStack}. + */ + final Rect mInsetFrame = new Rect(); + boolean mContentChanged; // If a window showing a wallpaper: the requested offset for the @@ -614,8 +620,18 @@ final class WindowState implements WindowManagerPolicy.WindowState { // We use the parent frame as the containing frame for fullscreen and child windows mContainingFrame.set(pf); mDisplayFrame.set(df); + mInsetFrame.setEmpty(); } else { task.getBounds(mContainingFrame); + task.getTempInsetBounds(mInsetFrame); + if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) { + + // If the bounds are frozen, we still want to translate the window freely and only + // freeze the size. + Rect frozen = mAppToken.mFrozenBounds.peek(); + mContainingFrame.right = mContainingFrame.left + frozen.width(); + mContainingFrame.bottom = mContainingFrame.top + frozen.height(); + } final WindowState imeWin = mService.mInputMethodWindow; if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this && mContainingFrame.bottom > cf.bottom) { @@ -674,6 +690,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { mOutsets.set(0, 0, 0, 0); } + // Denotes the actual frame used to calculate the insets. When resizing in docked mode, + // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the + // notion of a task having a different inset frame, we can achieve that while still moving + // the task around. + final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame; + // Make sure the content and visible frames are inside of the // final window frame. if (freeformWorkspace && !mFrame.isEmpty()) { @@ -709,42 +731,69 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContentFrame.set(mFrame); } } else { - mContentFrame.set(Math.max(mContentFrame.left, mFrame.left), - Math.max(mContentFrame.top, mFrame.top), - Math.min(mContentFrame.right, mFrame.right), - Math.min(mContentFrame.bottom, mFrame.bottom)); - - mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left), - Math.max(mVisibleFrame.top, mFrame.top), - Math.min(mVisibleFrame.right, mFrame.right), - Math.min(mVisibleFrame.bottom, mFrame.bottom)); - - mStableFrame.set(Math.max(mStableFrame.left, mFrame.left), - Math.max(mStableFrame.top, mFrame.top), - Math.min(mStableFrame.right, mFrame.right), - Math.min(mStableFrame.bottom, mFrame.bottom)); - } - - mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0), - Math.max(mOverscanFrame.top - mFrame.top, 0), - Math.max(mFrame.right - mOverscanFrame.right, 0), - Math.max(mFrame.bottom - mOverscanFrame.bottom, 0)); - - mContentInsets.set(mContentFrame.left - mFrame.left, - mContentFrame.top - mFrame.top, - mFrame.right - mContentFrame.right, - mFrame.bottom - mContentFrame.bottom); - - mVisibleInsets.set(mVisibleFrame.left - mFrame.left, - mVisibleFrame.top - mFrame.top, - mFrame.right - mVisibleFrame.right, - mFrame.bottom - mVisibleFrame.bottom); - - mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0), - Math.max(mStableFrame.top - mFrame.top, 0), - Math.max(mFrame.right - mStableFrame.right, 0), - Math.max(mFrame.bottom - mStableFrame.bottom, 0)); + mContentFrame.set(Math.max(mContentFrame.left, frame.left), + Math.max(mContentFrame.top, frame.top), + Math.min(mContentFrame.right, frame.right), + Math.min(mContentFrame.bottom, frame.bottom)); + + mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left), + Math.max(mVisibleFrame.top, frame.top), + Math.min(mVisibleFrame.right, frame.right), + Math.min(mVisibleFrame.bottom, frame.bottom)); + + mStableFrame.set(Math.max(mStableFrame.left, frame.left), + Math.max(mStableFrame.top, frame.top), + Math.min(mStableFrame.right, frame.right), + Math.min(mStableFrame.bottom, frame.bottom)); + } + + mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), + Math.max(mOverscanFrame.top - frame.top, 0), + Math.max(frame.right - mOverscanFrame.right, 0), + Math.max(frame.bottom - mOverscanFrame.bottom, 0)); + + mContentInsets.set(mContentFrame.left - frame.left, + mContentFrame.top - frame.top, + frame.right - mContentFrame.right, + frame.bottom - mContentFrame.bottom); + + mVisibleInsets.set(mVisibleFrame.left - frame.left, + mVisibleFrame.top - frame.top, + frame.right - mVisibleFrame.right, + frame.bottom - mVisibleFrame.bottom); + + if (mAttrs.type == TYPE_DOCK_DIVIDER) { + + // For the docked divider, we calculate the stable insets like a full-screen window + // so it can use it to calculate the snap positions. + mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0), + Math.max(mStableFrame.top - mDisplayFrame.top, 0), + Math.max(mDisplayFrame.right - mStableFrame.right, 0), + Math.max(mDisplayFrame.bottom - mStableFrame.bottom, 0)); + } else { + mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0), + Math.max(mStableFrame.top - frame.top, 0), + Math.max(frame.right - mStableFrame.right, 0), + Math.max(frame.bottom - mStableFrame.bottom, 0)); + } + if (!mInsetFrame.isEmpty()) { + mContentFrame.set(mFrame); + mContentFrame.top += mContentInsets.top; + mContentFrame.bottom += mContentInsets.bottom; + mContentFrame.left += mContentInsets.left; + mContentFrame.right += mContentInsets.right; + mVisibleFrame.set(mFrame); + mVisibleFrame.top += mVisibleInsets.top; + mVisibleFrame.bottom += mVisibleInsets.bottom; + mVisibleFrame.left += mVisibleInsets.left; + mVisibleFrame.right += mVisibleInsets.right; + mStableFrame.set(mFrame); + mStableFrame.top += mStableInsets.top; + mStableFrame.bottom += mStableInsets.bottom; + mStableFrame.left += mStableInsets.left; + mStableFrame.right += mStableInsets.right; + } mCompatFrame.set(mFrame); if (mEnforceSizeCompat) { // If there is a size compatibility scale being applied to the @@ -1558,12 +1607,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { return isVisibleOrAdding() && (mViewVisibility == View.VISIBLE) && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) - && stackCanReceiveKeys(); - } - - boolean stackCanReceiveKeys() { - final TaskStack stack = getStack(); - return stack != null && StackId.canReceiveKeys(stack.mStackId); + && (mAppToken == null || mAppToken.windowsAreFocusable()); } @Override @@ -1955,8 +1999,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { // isDragResizing() or isDragResizeChanged() is true. boolean resizing = isDragResizing() || isDragResizeChanged(); final Rect backDropFrame = (inFreeformWorkspace() || !resizing) ? frame : mTmpRect; - mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, - outsets, reportDraw, newConfig, backDropFrame); + mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets, + reportDraw, newConfig, backDropFrame); } public void registerFocusObserver(IWindowFocusObserver observer) { @@ -2003,7 +2047,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (task.isDragResizing()) { return true; } - return mDisplayContent.mDividerControllerLocked.isResizing() && + + // If the bounds are currently frozen, it means that the layout size that the app sees + // and the bounds we clip this window to might be different. In order to avoid holes, we + // simulate that we are still resizing so the app fills the hole with the resizing + // background. + return (mDisplayContent.mDividerControllerLocked.isResizing() + || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) && !task.inFreeformWorkspace() && !task.isFullscreen(); } @@ -2302,10 +2352,15 @@ final class WindowState implements WindowManagerPolicy.WindowState { } void setReplacing(boolean animate) { - if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) == 0) { - mWillReplaceWindow = true; - mReplacingWindow = null; - mAnimateReplacingWindow = animate; + if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0 + || mAttrs.type == TYPE_APPLICATION_STARTING) { + // We don't set replacing on starting windows since they are added by window manager and + // not the client so won't be replaced by the client. + return; } + + mWillReplaceWindow = true; + mReplacingWindow = null; + mAnimateReplacingWindow = animate; } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 381db5609048..83ab19094fb0 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -37,6 +37,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.localLOGV; +import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; @@ -1063,7 +1064,16 @@ class WindowStateAnimator { final int top = w.mYOffset + w.mFrame.top; // Initialize the decor rect to the entire frame. - mSystemDecorRect.set(0, 0, width, height); + if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) { + + // If we are resizing with the divider, the task bounds might be smaller than the + // stack bounds. The system decor is used to clip to the task bounds, which we don't + // want in this case in order to avoid holes. + final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); + mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } else { + mSystemDecorRect.set(0, 0, width, height); + } // If a freeform window is animating from a position where it would be cutoff, it would be // cutoff during the animation. We don't want that, so for the duration of the animation diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 6ebda0727165..feeab27e8fae 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -739,13 +739,15 @@ class WindowSurfacePlacer { } } } - /* - * Updates the shown frame before we set up the surface. This is needed because - * the resizing could change the top-left position (in addition to size) of the - * window. setSurfaceBoundariesLocked uses mShownPosition to position the - * surface. - */ - winAnimator.computeShownFrameLocked(); + if (!winAnimator.isAnimating()) { + // Updates the shown frame before we set up the surface. This is needed + // because the resizing could change the top-left position (in addition to + // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to + // position the surface. We only apply it to windows that aren't animating, + // because we depend on the animation to calculate the correct shown frame + // on the next animation step. + winAnimator.computeShownFrameLocked(); + } winAnimator.setSurfaceBoundariesLocked(recoveringMemory); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0bbd23e0d80e..dd6493c585f7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1322,7 +1322,7 @@ public final class SystemServer { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")); - intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED); + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); //Slog.d(TAG, "Starting service: " + intent); context.startServiceAsUser(intent, UserHandle.SYSTEM); } diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java index df7b412a27fe..264088903777 100644 --- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java @@ -35,7 +35,7 @@ import java.util.ArrayList; public class RankingHelperTest extends AndroidTestCase { @Mock NotificationUsageStats mUsageStats; - @Mock Handler handler; + @Mock RankingHandler handler; private Notification mNotiGroupGSortA; private Notification mNotiGroupGSortB; diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 5d5df959d962..aa95e1d69a10 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -242,8 +242,13 @@ public final class Call { */ public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010; + /** + * Whether the call is associated with the work profile. + */ + public static final int PROPERTY_WORK_CALL = 0x00000020; + //****************************************************************************************** - // Next PROPERTY value: 0x00000020 + // Next PROPERTY value: 0x00000040 //****************************************************************************************** private final Uri mHandle; diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java index 27ee804bebd4..a915d37d653b 100644 --- a/telecomm/java/android/telecom/DefaultDialerManager.java +++ b/telecomm/java/android/telecom/DefaultDialerManager.java @@ -155,8 +155,7 @@ public class DefaultDialerManager { // Get the list of apps registered for the DIAL intent with empty scheme Intent intent = new Intent(Intent.ACTION_DIAL); - List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, - PackageManager.MATCH_ENCRYPTION_DEFAULT); + List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0); List<String> packageNames = new ArrayList<>(); @@ -209,7 +208,7 @@ public class DefaultDialerManager { final List<String> result = new ArrayList<>(); final List<ResolveInfo> resolveInfoList = context.getPackageManager() - .queryIntentActivities(intent, PackageManager.MATCH_ENCRYPTION_DEFAULT); + .queryIntentActivities(intent, 0); final int length = resolveInfoList.size(); for (int i = 0; i < length; i++) { final ActivityInfo info = resolveInfoList.get(i).activityInfo; diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 5087080ebdf8..426b240e9c78 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -49,7 +49,7 @@ import java.util.List; * <pre> * {@code * <service android:name="your.package.YourInCallServiceImplementation" - * android:permission="android.permission.BIND_IN_CALL_SERVICE"> + * android:permission="android.permission.BIND_INCALL_SERVICE"> * <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /> * <intent-filter> * <action android:name="android.telecom.InCallService"/> diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 8537f9cf2f86..c680999e6fb9 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -153,6 +153,17 @@ public class ServiceState implements Parcelable { * @hide */ public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18; + + /** @hide */ + public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK = + (1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_IS95B - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_1xRTT - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_0 - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_A - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1)) + | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1)); + /** * Available registration states for GSM, UMTS and CDMA. */ @@ -1141,16 +1152,8 @@ public class ServiceState implements Parcelable { } /** @hide */ - public static boolean hasCdma(int radioTechnologyBitmask) { - int cdmaBitmask = (RIL_RADIO_TECHNOLOGY_IS95A - | RIL_RADIO_TECHNOLOGY_IS95B - | RIL_RADIO_TECHNOLOGY_1xRTT - | RIL_RADIO_TECHNOLOGY_EVDO_0 - | RIL_RADIO_TECHNOLOGY_EVDO_A - | RIL_RADIO_TECHNOLOGY_EVDO_B - | RIL_RADIO_TECHNOLOGY_EHRPD); - - return ((radioTechnologyBitmask & cdmaBitmask) != 0); + public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) { + return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0; } /** @hide */ diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 2813df537457..2cef1b3273a9 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -34,7 +34,6 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; -import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageItemInfo; @@ -820,7 +819,7 @@ public class MockPackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, - ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + ContainerEncryptionParams encryptionParams) { throw new UnsupportedOperationException(); } @@ -957,7 +956,7 @@ public class MockPackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, - Uri verificationURI, ManifestDigest manifestDigest, + Uri verificationURI, ContainerEncryptionParams encryptionParams) { throw new UnsupportedOperationException(); } diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 3c2659f6ab59..b78fd494e555 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -93,7 +93,7 @@ public class WindowManagerPermissionTests extends TestCase { try { mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null, - Configuration.EMPTY, false); + Configuration.EMPTY, false, false); fail("IWindowManager.addAppToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 40437fac0092..01ee18b1649c 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -76,7 +76,7 @@ public class IWindowManagerImpl implements IWindowManager { @Override public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4, boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10, - Rect arg11, Configuration arg12, boolean arg13) throws RemoteException { + Rect arg11, Configuration arg12, boolean arg13, boolean arg14) throws RemoteException { // TODO Auto-generated method stub } @@ -543,6 +543,11 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) { + public void registerDockedStackListener(IDockedStackListener listener) throws RemoteException { + } + + @Override + public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) + throws RemoteException { } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 683c4aabf6d9..c8e3d03169e8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -183,7 +183,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private static LayoutLog sCurrentLog = sDefaultLog; - private static final int LAST_SUPPORTED_FEATURE = Features.CHOREOGRAPHER; + private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR; @Override public int getApiLevel() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index a2fad13e7b2c..3f9574f3ba83 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -34,7 +34,6 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; -import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageItemInfo; @@ -522,7 +521,7 @@ public class BridgePackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, - ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + ContainerEncryptionParams encryptionParams) { } @Override @@ -544,7 +543,7 @@ public class BridgePackageManager extends PackageManager { @Override public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, - ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { + ContainerEncryptionParams encryptionParams) { } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 9c89bfe2a28f..dfbc69bee59c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -19,9 +19,6 @@ package com.android.layoutlib.bridge.bars; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.resources.Density; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; import android.widget.LinearLayout; @@ -41,29 +38,18 @@ public class NavigationBar extends CustomBar { private static final int WIDTH_DEFAULT = 36; private static final int WIDTH_SW360 = 40; private static final int WIDTH_SW600 = 48; - private static final String LAYOUT_XML = "/bars/navigation_bar.xml"; + protected static final String LAYOUT_XML = "/bars/navigation_bar.xml"; private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml"; - - /** - * Constructor to be used when creating the {@link NavigationBar} as a regular control. - * This is currently used by the theme editor. - */ - @SuppressWarnings("unused") - public NavigationBar(Context context, AttributeSet attrs) { - this((BridgeContext) context, - Density.getEnum(((BridgeContext) context).getMetrics().densityDpi), - LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically - ((BridgeContext) context).getConfiguration().getLayoutDirection() == - View.LAYOUT_DIRECTION_RTL, - (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0, - 0); + public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, + boolean rtlEnabled, int simulatedPlatformVersion) { + this(context, density, orientation, isRtl, rtlEnabled, simulatedPlatformVersion, + getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML); } - public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, - boolean rtlEnabled, int simulatedPlatformVersion) { - super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, - "navigation_bar.xml", simulatedPlatformVersion); + protected NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl, + boolean rtlEnabled, int simulatedPlatformVersion, String layoutPath) { + super(context, orientation, layoutPath, "navigation_bar.xml", simulatedPlatformVersion); int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT); setBackgroundColor(color == 0 ? 0xFF000000 : color); @@ -117,7 +103,7 @@ public class NavigationBar extends CustomBar { view.setLayoutParams(layoutParams); } - private static int getSidePadding(float sw) { + protected int getSidePadding(float sw) { if (sw >= 400) { return PADDING_WIDTH_SW400; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java new file mode 100644 index 000000000000..043528016c2d --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.layoutlib.bridge.bars; + +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.resources.Density; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +/** + * Navigation Bar for the Theme Editor preview. + * + * For small bars, it is identical to {@link NavigationBar}. + * But wide bars from {@link NavigationBar} are too wide for the Theme Editor preview. + * To solve that problem, {@link ThemePreviewNavigationBar} use the layout for small bars, + * and have no padding on the sides. That way, they have a similar look as the true ones, + * and they fit in the Theme Editor preview. + */ +public class ThemePreviewNavigationBar extends NavigationBar { + private static final int PADDING_WIDTH_SW600 = 0; + + @SuppressWarnings("unused") + public ThemePreviewNavigationBar(Context context, AttributeSet attrs) { + super((BridgeContext) context, + Density.getEnum(((BridgeContext) context).getMetrics().densityDpi), + LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically + ((BridgeContext) context).getConfiguration().getLayoutDirection() == + View.LAYOUT_DIRECTION_RTL, + (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0, + 0, LAYOUT_XML); + } + + @Override + protected int getSidePadding(float sw) { + if (sw >= 600) { + return PADDING_WIDTH_SW600; + } + return super.getSidePadding(sw); + } +} |