Merge "Add support message for device admins"
diff --git a/Android.mk b/Android.mk
index d3e1cf5..c1c74ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -226,7 +226,6 @@
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 @@
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 0aaa46f..4eb8c47 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -67,7 +67,6 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 9d4117a..fd94679 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -93,7 +93,6 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 6ee1eb8..2b6077b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -67,7 +67,6 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 4f7a109..6302d74 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 @@
" 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 @@
"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 @@
case "resize":
runStackResize();
break;
+ case "resize-docked-stack":
+ runStackResizeDocked();
+ break;
case "positiontask":
runStackPositionTask();
break;
@@ -1751,6 +1759,20 @@
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 3e6b595..6fc0d74 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1082,6 +1082,10 @@
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 800ebc6..8346161 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1703,6 +1703,10 @@
mSearchManager.stopSearch();
}
+ if (mActionBar != null) {
+ mActionBar.onDestroy();
+ }
+
getApplication().dispatchActivityDestroyed(this);
}
@@ -2208,14 +2212,22 @@
* @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 3a3aa92..da21099 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -332,10 +332,9 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 cee1aa5..e596a68 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 @@
* 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 @@
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 @@
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 @@
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 @@
}
/** @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 @@
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 ed168d1..177fabe 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4161,6 +4161,15 @@
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 @@
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 @@
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 77721e6..a60fbf6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -42,7 +42,6 @@
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 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 @@
@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 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 @@
@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 3fbbdff..8d7f347 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 @@
/**
* 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.
+ * 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,
- 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.
- */
- 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 @@
}
/**
- * 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 @@
/**
* 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 @@
| 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 @@
}
}
};
+
+ /**
+ * 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 61b1bc8..ceb14ad 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -92,7 +92,7 @@
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 @@
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 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 @@
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 abe9822..e60cb03 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -78,6 +78,8 @@
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 4b0935c..560e22a 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -18,5 +18,9 @@
/** @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 85d9831..9a3c820 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -637,10 +637,12 @@
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 @@
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 0f3ca10..c3dfab4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4169,11 +4169,18 @@
/**
* 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 326735e..dedf07f5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -281,6 +281,29 @@
* {@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 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 @@
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 @@
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 @@
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 @@
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 e7dc764..0000000
--- 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 69f508e..bccc3d9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -93,6 +93,109 @@
}
/**
+ * 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 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 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 @@
* 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 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 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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
*
* @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 @@
* @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 @@
* @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 @@
*
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
* <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 @@
* @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 @@
* 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 @@
* @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 @@
* @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 @@
// @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 @@
* @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 void installPackageWithVerification(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
- Uri verificationURI, ManifestDigest manifestDigest,
+ Uri verificationURI,
ContainerEncryptionParams encryptionParams);
/**
@@ -4423,7 +4549,7 @@
* @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 f445cf8..236cf64a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1032,31 +1032,6 @@
}
/**
- * 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 @@
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;
- a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
- R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+ }
+ }
+
+ 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 @@
/* 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 cfb4473..945858e6 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -24,6 +24,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -50,6 +51,10 @@
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 e5119b6..f90d295 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 @@
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 @@
* 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 @@
return mOriginatingUid;
}
- public ManifestDigest getManifestDigest() {
- return mManifestDigest;
- }
-
/** @return NO_UID when not set */
public int getInstallerUid() {
return mInstallerUid;
@@ -155,14 +141,6 @@
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 @@
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 @@
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 @@
dest.writeParcelable(mOriginatingURI, 0);
dest.writeParcelable(mReferrer, 0);
dest.writeInt(mOriginatingUid);
- dest.writeParcelable(mManifestDigest, 0);
dest.writeInt(mInstallerUid);
}
@@ -221,7 +195,6 @@
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 7669053..ee6aec2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -698,6 +698,18 @@
*/
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 60c6e82..b01633e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1976,7 +1976,21 @@
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 b883f9c..4eaee0b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6374,6 +6374,13 @@
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 46aa1af..37ec725 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -68,7 +68,6 @@
* 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 71d9d5d..4de36cd 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -120,6 +120,32 @@
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 0000000..e7d17c2
--- /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 5ebc7ac..0f66873 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -40,5 +40,6 @@
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 5c5f358..0000000
--- 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 e6bf6ba..a0de17f 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,6 +23,7 @@
/** @hide */
oneway interface INotificationListener
{
+ // listeners and assistants
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
@@ -31,4 +32,11 @@
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 c5f0db9..0000000
--- 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 7ce5e1f..3a8956e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -20,8 +20,11 @@
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 @@
* </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 @@
/** 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 @@
* @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 @@
* @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 @@
* 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 @@
/**
* 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 1e62edc..b42d9ea 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 @@
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 @@
@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 @@
// 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 @@
@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 @@
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 @@
}
}
- 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 @@
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 @@
/**
* 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 541623d..4688843 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -79,6 +79,7 @@
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 @@
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 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 @@
}
allowPeek = source.readInt() == 1;
allowLights = source.readInt() == 1;
+ allowScreenOn = source.readInt() == 1;
}
@Override
@@ -185,6 +189,7 @@
}
dest.writeInt(allowPeek ? 1 : 0);
dest.writeInt(allowLights ? 1 : 0);
+ dest.writeInt(allowScreenOn ? 1 : 0);
}
@Override
@@ -200,6 +205,7 @@
.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 @@
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 @@
&& 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 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 @@
}
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 @@
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 @@
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 @@
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 7e70501..75da82f 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -24,4 +24,5 @@
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 63a4c5e..4997f75 100644
--- a/core/java/android/service/quicksettings/IQSTileService.aidl
+++ b/core/java/android/service/quicksettings/IQSTileService.aidl
@@ -22,6 +22,7 @@
* @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 a53fc59..6104913 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -37,11 +37,12 @@
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 @@
/**
* @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 @@
}
/**
+ * @hide
+ */
+ public IQSService getQsService() {
+ return mService;
+ }
+
+ /**
* Gets the current icon for the tile.
*/
public Icon getIcon() {
@@ -137,21 +151,8 @@
}
}
- /**
- * @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 @@
}
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 d8787b4..1e134c7 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.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 @@
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 @@
* 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 @@
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 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 @@
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 @@
@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 @@
}
}
}
+
+ /**
+ * 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 1becfb4..f22cde0 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -28,12 +28,6 @@
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 @@
}
}
+ 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 @@
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 @@
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 @@
}
}
+ /**
+ * 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
similarity index 70%
rename from core/java/android/view/IDockDividerVisibilityListener.aidl
rename to core/java/android/view/IDockedStackListener.aidl
index a7d5cda..77fa7e2 100644
--- a/core/java/android/view/IDockDividerVisibilityListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -22,6 +22,15 @@
*
* @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 7a379d50..84d312d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -29,7 +29,7 @@
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 @@
* 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 @@
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 aa29636..914bd56 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -555,6 +555,26 @@
}
}
+ 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 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 cd93dab..1c24392 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1419,8 +1419,9 @@
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 @@
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 f2b4fb3..1c9f3b4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3315,6 +3315,7 @@
&& 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 19e290b..7e98193 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1467,10 +1467,14 @@
}
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 @@
}
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 @@
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 4b6e7e4..575ef02 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -483,6 +483,12 @@
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 bfc56db..4b821ab 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -36,6 +36,8 @@
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 4a0f3fc..d8ec22a 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -587,10 +587,6 @@
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 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 @@
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 ab0df55..7c8dbe8 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 654d148..0a25a0a 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -313,94 +313,10 @@
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 90606a35..3473d9d 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -578,7 +578,7 @@
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 @@
return NULL;
}
- am->getLocales(&locales);
+ am->getLocales(&locales, includeSystemLocales);
const int N = locales.size();
@@ -608,6 +608,16 @@
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 @@
// 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 1090f90..c154e91 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 0000000..7308dc5
--- /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 5a007ce..7f57ded 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 2a11081..58a77e8 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 c03d471..d13a622 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 ef6b467..37b2c12 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 eee5771..3cfd9f4 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 347f2f9..be9ba62 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 3c1d944..845c8c9 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 7cd25af..ee8921e 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -22,7 +22,7 @@
$(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 5177836..c0453f8 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 0000000..a79f87d
--- /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 0000000..c83f5f1
--- /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 04b0478..ef915bb 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 37495e1..0000000
--- 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 9b216cb..d963812 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 @@
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 @@
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 @@
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);
+ REFERRER, ORIGINATING_UID);
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);
-
- 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 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);
+ REFERRER, ORIGINATING_UID);
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);
-
- 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 0000000..55c0031
--- /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 0000000..c3b25a1
--- /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 0000000..513e40f
--- /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 554b6f1..731bf42 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 da58884..175c726 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -163,8 +163,11 @@
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 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 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 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 1857345..84ca546 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,6 +19,7 @@
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 @@
/** 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 @@
@Override
public void draw(Canvas canvas) {
mAnimatedVectorState.mVectorDrawable.draw(canvas);
- if (isStarted()) {
- invalidateSelf();
- }
}
@Override
@@ -486,6 +493,7 @@
* 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 @@
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 @@
@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 3d4e47d..914ac3d 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -100,16 +100,17 @@
* 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 @@
*/
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 @@
* 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 @@
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 49b6333..428a2b8 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1552,9 +1552,9 @@
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 @@
// 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 @@
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 @@
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 8a03b94..6913f43 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -176,7 +176,8 @@
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 @@
}
delete manifestAsset;
+ ap.isSystemAsset = isSystemAsset;
mAssetPaths.add(ap);
// new paths are always added at the end
@@ -233,6 +235,7 @@
// 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 @@
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 @@
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 @@
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 21b543e..44f92c7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3080,13 +3080,16 @@
// 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 @@
// 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 @@
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 @@
}
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::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 @@
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 @@
}
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 @@
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::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::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 @@
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 @@
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 f48c509..11056d4 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -108,6 +108,7 @@
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 0f0768f..332a204 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -79,7 +79,9 @@
.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 @@
.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 @@
};
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 @@
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 @@
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::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 f8282dc..a0d5fae 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -60,27 +60,67 @@
}
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 @@
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::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 f158e8b..65e8b29 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -27,6 +27,7 @@
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 @@
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 @@
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 @@
// 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 0000000..e6b943a
--- /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 b12c0c9..9df4e3a 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -52,89 +52,35 @@
*/
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);
+ ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
+ const RecordedOp& recordedOp, bool expandForStroke);
- // 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);
- }
-
- 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 @@
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 @@
};
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 @@
}
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 @@
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 cf76e6b..cf2726b 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -45,6 +45,21 @@
}
}
+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 4709ef4..b9e87ae 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -80,6 +80,12 @@
* 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 @@
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 5f166ca..160090d 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 @@
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 @@
*/
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::clipPathWithTransform(const SkPath& path, const mat4* transform,
SkRegion::Op op) {
+ onClipUpdated();
SkMatrix skTransform;
transform->copyTo(skTransform);
SkPath transformed;
@@ -241,7 +254,7 @@
// 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 @@
// 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::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 @@
}
}
+/**
+ * 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 268301c..479796d 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 @@
return mTransform;
}
+ void transform(const Matrix4& transform) {
+ Matrix4 t;
+ t.loadMultiply(transform, mTransform);
+ mTransform = t;
+ }
+
private:
Rect mBounds;
Matrix4 mTransform;
@@ -66,27 +74,62 @@
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 @@
}
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 @@
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 @@
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 ff4dc4a..9994498 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -47,6 +47,7 @@
#if HWUI_NEW_OPS
class BakedOpState;
class BakedOpRenderer;
+struct ClipBase;
#else
class OpenGLRenderer;
#endif
@@ -57,7 +58,7 @@
#if HWUI_NEW_OPS
BakedOpRenderer* renderer,
const BakedOpState* bakedState,
- const Rect* clip,
+ const ClipBase* clip,
#else
OpenGLRenderer* renderer,
#endif
@@ -81,7 +82,7 @@
#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 bcf819e..e72f396 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -64,7 +64,7 @@
// 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 2507ff3..45fc16c 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -78,7 +78,7 @@
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 ad9559f..11b2c8a 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -460,7 +460,7 @@
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 @@
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 @@
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 @@
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 @@
const OvalOp* resolvedOp = new (mAllocator) OvalOp(
unmappedBounds,
op.localMatrix,
- op.localClipRect,
+ op.localClip,
op.paint);
deferOvalOp(*resolvedOp);
}
@@ -829,7 +829,7 @@
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 @@
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 dbbce8b..4e9b5e6 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -192,7 +192,7 @@
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 @@
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 cfdd0d2..1971530 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -33,6 +33,7 @@
namespace android {
namespace uirenderer {
+struct ClipBase;
class OffscreenBuffer;
class RenderNode;
struct Vertex;
@@ -91,10 +92,10 @@
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 @@
/* 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 @@
: opId(opId)
, unmappedBounds(unmappedBounds)
, localMatrix(localMatrix)
- , localClipRect(localClipRect)
+ , localClip(localClip)
, paint(paint) {}
};
@@ -187,9 +188,9 @@
};
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 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 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 @@
Matrix4 shadowMatrixZ;
const float casterAlpha;
const SkPath* casterPath;
+ const Rect localClipRect;
const Vector3 lightCenter;
};
@@ -374,7 +377,7 @@
*/
struct EndLayerOp : RecordedOp {
EndLayerOp()
- : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), Rect(), nullptr) {}
+ : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {}
};
/**
@@ -388,13 +391,13 @@
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 f75d8d4..f7f6caf 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -39,7 +39,7 @@
"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 @@
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 @@
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::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 @@
addOp(new (alloc()) PointsOp(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
}
@@ -264,7 +265,7 @@
addOp(new (alloc()) LinesOp(
calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
}
@@ -272,7 +273,7 @@
addOp(new (alloc()) RectOp(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint)));
}
@@ -305,7 +306,7 @@
addOp(new (alloc()) SimpleRectsOp(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(paint), rectData, vertexCount));
}
@@ -339,7 +340,7 @@
addOp(new (alloc()) RoundRectOp(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint), rx, ry));
}
@@ -358,7 +359,7 @@
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 @@
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 @@
addOp(new (alloc()) OvalOp(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint)));
}
@@ -399,7 +400,7 @@
addOp(new (alloc()) ArcOp(
Rect(left, top, right, bottom),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint),
startAngle, sweepAngle, useCenter));
}
@@ -408,7 +409,7 @@
addOp(new (alloc()) PathOp(
Rect(path.getBounds()),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(&paint), refPath(&path)));
}
@@ -459,7 +460,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
addOp(new (alloc()) BitmapOp(
Rect(bitmap->width(), bitmap->height()),
*(mState.currentSnapshot()->transform),
- mState.getRenderTargetClipBounds(),
+ getRecordedClip(),
refPaint(paint), refBitmap(*bitmap)));
}
@@ -528,7 +529,7 @@
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 @@
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 470f9ec..1a2ac97f 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 @@
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 194aa57..5fac3a1 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -167,6 +167,7 @@
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 95dcd18..61dd8c3 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 @@
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 b37ec58..f302244 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 0000000..db6402c
--- /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 ac78124..935ddcf 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 f9f5316..3fd822d 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 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, 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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c4d305e..4cae737 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -119,5 +119,122 @@
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 78d65dd..5c44290 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -27,7 +27,7 @@
int two = 2;
};
-TEST(LinearAllocator, alloc) {
+TEST(LinearAllocator, create) {
LinearAllocator la;
EXPECT_EQ(0u, la.usedSize());
la.alloc(64);
@@ -35,7 +35,7 @@
// 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 @@
{
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 @@
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 b28e436..0d13118 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -64,7 +64,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 08f927c..a63cb18 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -33,6 +33,14 @@
}
}
+#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// ...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 e1c6f6c..dcbc0dd 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -56,12 +56,12 @@
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
deleted file mode 100644
index 65e42aa..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 1de8292..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 00b8a8b7..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 5236774..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index c7daa2a..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
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 27cfa81d..0000000
--- 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/DocumentsUI/res/drawable/ic_root_smartphone.xml b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml
new file mode 100644
index 0000000..3021b16
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<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 b522e0a..c303e0c 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 4eacee5..6719b23 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -114,11 +114,7 @@
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 c81d4fb..46372c0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -36,7 +36,7 @@
/** 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 0d5e34e..9617582 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.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.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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}.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 @@
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 @@
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 @@
}
}
- 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 0000000..43c2f63
--- /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 bea38c6..f2bade5 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.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 @@
@Nullable String info;
@Nullable String error;
- Model(Context context, RecyclerView.Adapter<?> viewAdapter) {
+ Model(Context context) {
mContext = context;
}
@@ -87,8 +86,19 @@
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 @@
@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 0000000..68756a3
--- /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 e47af67..5994df9 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 @@
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 @@
* @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 void onChanged() {
// TODO: This is causing b/22765812
mSelection.clear();
- mModelIds = mEnvironment.getModelIds();
+ mModelIds = mAdapter.getModelIds();
}
@Override
@@ -339,7 +339,10 @@
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 @@
* @param modelId
*/
public void toggleSelection(String modelId) {
+ checkNotNull(modelId);
boolean changed = false;
if (mSelection.contains(modelId)) {
changed = attemptDeselect(modelId);
@@ -387,7 +391,7 @@
return;
}
- if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(position))) {
+ if (mSelection.contains(mAdapter.getModelId(position))) {
mRanger = new Range(position);
}
}
@@ -404,7 +408,11 @@
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 @@
* @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 @@
* (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 @@
* @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 @@
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 @@
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 @@
}
@Override
- public String getModelIdFromAdapterPosition(int position) {
- return mAdapter.getModelId(position);
- }
-
- @Override
public void addOnScrollListener(RecyclerView.OnScrollListener listener) {
mView.addOnScrollListener(listener);
}
@@ -961,26 +959,6 @@
});
}
}
-
- @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 @@
mModelBuilder = new Runnable() {
@Override
public void run() {
- mModel = new GridModel(mEnvironment);
+ mModel = new GridModel(mEnvironment, mAdapter);
mModel.addOnSelectionChangedListener(BandController.this);
}
};
@@ -1088,7 +1066,7 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 0000000..b4782f0
--- /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 4caa891..12c0b8f 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 @@
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 0000000..5ce1823
--- /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 121eb41..bed7c9c 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.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 @@
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 @@
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 @@
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 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 5989135..e06199e 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 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 @@
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 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 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 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 3d6923f..5c04db9 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 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 com.android.documentsui.dirlist.MultiSelectManager.GridModel;
-import java.util.List;
+import java.util.ArrayList;
import java.util.Set;
@SmallTest
@@ -38,15 +37,28 @@
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 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 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 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 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 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 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 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 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 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 @@
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 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 0000000..c8d424f
--- /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 0000000..714062d
--- /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 0000000..267f47d
--- /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 0000000..3a537a6
--- /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 fc85f2b..c4cfd3a 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 @@
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 @@
public class TestSelectionEnvironment implements SelectionEnvironment {
- private List<String> mItems;
-
public TestSelectionEnvironment(List<String> items) {
- mItems = items;
}
@Override
@@ -114,27 +110,4 @@
@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 82fd512..3dc5d7e 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 @@
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 0d9d79c..ebe8752 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 1c9b0a9..e33d0fa 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 fc11a6b..71761b9 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 4a5ea37..b5a7181 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 676c618..54d2dfa 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 8180261..95badc5 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 a5787f5..e4d97d7 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 8553ed9..93b6438 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 495f10f..beb8f94 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 82654bc..3923dbd 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 a2f750b..0b1238b 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 d426616..c3acca7 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 b8c8521..dae40d9 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 b8c8521..dae40d9 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 b8c8521..dae40d9 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 34ebef4..855b72c 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 cedd582..858c1b6 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 714004e..7f1056c 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 7f47bf3..fe5fc29 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 1f8b575..e1a35f7 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 e4b4b93..498f204 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 66c54a1..b32bfb7 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 e56940a..0190454 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 9bcf770..0efe805 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 6dff8fa..4b72648 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 9ce83fa..5224c38 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 dd02171..5cde233 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 9b3a386..3559f1e 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 27db9fe..42c9a05 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 0cea206..6988a73 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 b3509ca..8d912e9 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 9f897f7..2d98a74 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 2d44612..e175208 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 81a5eaf..bf9bf8a 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 e2dfd7e..da9d204 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 1b8e176..549711c 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 0186a68..aed4365 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 56f4006..50e5955 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 6ad3efd..ea5dc00 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 bf37ac4..6a47406 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 9e6a4b7..538a38c 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 2019646..014a6fb 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 2f9a83b..87da105 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 51c2568..b588c2c 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 f00103f..d76e87f 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 cc0e7cb..4aea631 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 b939ca0..ed57fb7 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 2e67752..1f72d04 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 002aed1..cd4812f 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 a496a21..ae0b15d 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 5c85c25..5885513 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 1afa444..1dd477d 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 ad3d3ed..85f9ffb 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 d58070e..9f7c022 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 3b28043..dd4b58d 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 c5f94cb..0cbe5e4 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 3b28043..dd4b58d 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 c76faf0..a666da0 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 f1cd182..e93bae1 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 eb13ce4..6753624 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 b5b6d2a..7db7835 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 cb107d7..ed37933 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 c176f18..7f4d6a8 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 6b66094..9596ce5 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 40e1593..7d0d0a0 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 a014e1d..d1041da 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 4f6ea57..e66f073 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 a6d2140..80b84f0 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 de2f33d..6826419 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 231079b..8f62326 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 c44d7f8..5e0839a 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 d5dbe90..2448b23 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 4c9c178..0834303 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 d138a28..d2d8b76 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 7ab8b02..178e301 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 509ff88..5b96383 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 f323667..db5f09b 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 c6ad832..787a442 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 cb3670f..bf3addf 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 2a4efbd..1bce7f9 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 7e22881..f7e25db 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
new file mode 100644
index 0000000..f3be2ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/SystemUI/res/drawable/docked_divider_handle.xml
deleted file mode 100644
index 84c0343..0000000
--- a/packages/SystemUI/res/drawable/docked_divider_handle.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2 (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.
--->
-<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
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index 22ed216..7ea5027 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 3cdee64..071b7c8 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 2b82b05..5b44c17 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 f81bf23..0187993 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 d96e790..503a767 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 b3e494c..d89d036 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 4efc5df..55f0841 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 f482e96..4056963 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 95c4a22..93c26a4 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 38885d5..939465b6 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 d27c3ee..e19aa0e 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 4f26dc1..b1cd561 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 3f868a8..2100c57 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 4c87758..1ad5b26 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 21e1eee..f56d630 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 fa8b6c33..f6fc997 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 fa8b6c33..f6fc997 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 fa8b6c33..f6fc997 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 54850f0..fd59418 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 41d83dc..223e684 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 9bb7898..f50e78c 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 e62303a..c481f52 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 f45c01b..3dee7c9 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 af1a1e5..4a5c302 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 20a8bdc..2a4f4d9 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 8e76ee8..928a8b3 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 4562d89b..2a951f7 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 69174ef..f331ef9 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 1ab7f71..b31c1e0 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 a67258c..3bef6e9 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 8589e95..8dc9a19 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 1b8de04..99d6980 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 9075484..f6124db 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 556018c..7ea1746 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 b49c387..b395f8b 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 e6ac5af..2c894f5 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 e7e3a86..580d995 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 79e0f4f..7a51668 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 5187b54..eb8995e 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 5665a90..5c10213 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 0326a67..28b2de5 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 b910c04..110376f 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 e703e00..ee2ef5d 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 8e43408..ed103ea 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 04066a7..ba98e2b 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 649d07b..0143fea 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 928bbcd..c573139 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 2f4b81a..c6a08e6 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 3dd084e..91c817a 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 59eadf1..5d3a96f 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 5dd221a..f0a013c 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 293e010..75008cb 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 ffc2fcf..295a49a 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 6f40d55..d0aa510 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 27eb1d11..75812ad 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 2dd91ec..fc5a3ea 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 bf222bc..8ef50c9 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 5d43efa..8f61478 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 44ecd41..8a727a1 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 5d43efa..8f61478 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 e9e7bdc..3ab8bf2 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 b68b434..6ffd333 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 b9f7e53..efbc042 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 91a143e..31985b4 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 96974ae..7c8d443 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 b904ae1..c8add94 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 6470fcc..bec1fd4 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 5f2b397..d4a9cdc 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 8f962da..2ffbf2b 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 a625c6f..168a11b 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 9d1403f..086dede 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 38e028c..8c1a7d3 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 5815041..4bee3ea 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 6832808..c0ecc78 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 9736028..3109403 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 28484eb..52aa580 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 4bd9dbc..a849cfc 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 c094d5b..fa835ba 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 3af77c2..bb8f4ad 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 8591785..0782354 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 34fa7aa..29520f6 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 ebd857c..3ee46ea 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 45ddd50..876c21e 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 3b0ab791..527b638 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 b56ad76..01eb5f9 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 @@
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 39f0c55..1a36abd 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 @@
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.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 @@
@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 @@
}
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 a5e1fd5..0000000
--- 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 87c2973..cdba3eb 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 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 @@
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 6706c7a..a6a7143 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 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
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
rename to packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index bb74f34..2e5a0b2 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 @@
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 @@
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 @@
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 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 @@
} catch (RemoteException e) {
}
}
- mUnbind.run();
}
@Override
@@ -154,16 +146,20 @@
@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 @@
@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 0000000..d41cdde
--- /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 0000000..41fce9f1
--- /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 0000000..2f77a30
--- /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 0000000..00f7699
--- /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 fc802dd..c696f88 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 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 f73ee35..23a15b9 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 @@
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 d96f735..4f9f46d 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 @@
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 12c1298..39d9da1 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 @@
}
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 @@
// 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 @@
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 @@
@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 250d567..55aa32b 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 @@
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 08540f6..e79aabf 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 @@
mHost.openPanels();
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
mController.setLocationEnabled(!wasEnabled);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
});
return;
@@ -76,8 +74,6 @@
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 d85cf60..7bce54b 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 @@
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 @@
}
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 @@
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 07915f8..255f29f 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 @@
}
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 @@
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 ffcc805..57074df 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.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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 7547570f..cfbd1cb 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 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 @@
/**
* 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 40c84ba..c323522 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 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 949fb86..fd00289 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 @@
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.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 @@
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 @@
// 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 @@
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 @@
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 35e53f6..108029d 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.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.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.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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
/**
* 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 @@
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 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 7a92b2a..d6262ac 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 @@
// 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 34a0e52..d030fc1 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 boolean isLaunchTarget;
public boolean isHistorical;
- public boolean lockToThisTask;
- public boolean lockToTaskEnabled;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -142,8 +140,8 @@
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 @@
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isHistorical = isHistorical;
- this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
- this.lockToTaskEnabled = lockToTaskEnabled;
}
/** Copies the other task. */
@@ -178,8 +174,6 @@
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 6f003ab..5e720cb 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 @@
removeGroup(group);
}
}
- // Update the lock-to-app state
- t.lockToThisTask = false;
}
/** Removes a task */
@@ -437,9 +435,6 @@
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 @@
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 9b1315a..c95c73b 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 @@
* 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 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 10df156..9d391b0 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 @@
* 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 830d607..94fae13 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.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 @@
List<TaskView> mImmutableTaskViews = new ArrayList<>();
List<TaskView> mTmpTaskViews = new ArrayList<>();
LayoutInflater mInflater;
+
boolean mTouchExplorationEnabled;
+ boolean mScreenPinningEnabled;
Interpolator mFastOutSlowInInterpolator;
@@ -224,6 +227,8 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
// 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 @@
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 a3e8b2d..14909c5 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 @@
setBackground(new FakeShadowDrawable(res, config));
}
setOutlineProvider(mViewBounds);
+ setOnLongClickListener(this);
}
/** Set callback */
@@ -175,8 +176,10 @@
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 @@
/** 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 @@
} 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 @@
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 @@
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 @@
.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 @@
setDim(getDimFromTaskProgress());
}
- /**** View focus state ****/
-
/**
* Explicitly sets the focused state of this task.
*/
@@ -672,19 +667,42 @@
}
}
+ /**
+ * 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 @@
// 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 @@
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 e1d80fd..eaef51c 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 @@
// 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 6ff7a3e..189e651 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 @@
putComponent(Divider.class, this);
mDockDividerVisibilityListener = new DockDividerVisibilityListener();
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.registerDockDividerVisibilityListener(mDockDividerVisibilityListener);
+ ssp.registerDockedStackListener(mDockDividerVisibilityListener);
}
@Override
@@ -94,10 +95,15 @@
});
}
- 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 0000000..5ef56f3
--- /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 5af172c..e43d531 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 @@
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 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 @@
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 13642eb..109cf47 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.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.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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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 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 @@
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 @@
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
: mVelocityTracker.getXVelocity());
+ mMoving = false;
break;
}
return true;
@@ -197,14 +250,16 @@
}
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 @@
} else {
mWindowManagerProxy.maximizeDockedStack();
}
+ mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
}
private void liftBackground() {
@@ -245,29 +301,33 @@
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 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 2251874..161f873 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 @@
| 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 ef47d8d..2791dfc 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 @@
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 @@
}
};
+ 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 @@
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 @@
}
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 1d7651c..5906bda 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 @@
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 @@
((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 @@
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 @@
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 f7680a7..3cc1ab9 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 @@
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 58c9722..d0c14f1 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 @@
// 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 @@
}
});
}
+
+ @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 79bd626..cc85d0f 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.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 @@
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 @@
}
} 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 cddb1fc..a3d0ce6 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.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 @@
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 @@
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 80fcba60e..78d09e3 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 @@
}
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 @@
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 e6d837a..5c856e8 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 @@
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.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 @@
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 @@
ht.start();
mLooper = ht.getLooper();
+ mServices = new TileServices(this, mLooper);
+
TunerService.get(mContext).addTunable(this, TILES_SETTING);
}
@@ -256,6 +254,10 @@
return mSecurity;
}
+ public TileServices getTileServices() {
+ return mServices;
+ }
+
@Override
public void onTuningChanged(String key, String newValue) {
if (!TILES_SETTING.equals(key)) {
@@ -306,50 +308,6 @@
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 @@
// 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 1372cca..a91f6a2 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.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 @@
@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 @@
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 1e3b0f1..9081af1 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 @@
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 9d4ec10..3c63aae 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 @@
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 cd47ac1..38d8de0 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 @@
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 c21af24..2825601 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 0000000..6ebf488
--- /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 0000000..4586c28
--- /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 0000000..7a3ce87
--- /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 3e8dc4e..6171694 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 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.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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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;
- }
- }
- mFocusedActivity = null;
- EventLogTags.writeAmFocusedActivity(-1, "NULL", "clearFocusedActivity");
+ 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;
+ }
+ }
+
+ // 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 @@
ActivityRecord r = stack.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedStack");
- mStackSupervisor.resumeTopActivitiesLocked(stack, null, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -2844,7 +2877,7 @@
ActivityRecord r = task.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedTask");
- mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -2856,7 +2889,8 @@
/** 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 @@
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 @@
}
@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 @@
}
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 @@
}
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 @@
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r, false)) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
Binder.restoreCallingIdentity(origId);
@@ -4811,12 +4849,11 @@
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 @@
// Clean-up disabled activities.
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, disabledClasses, true, false, userId) && mBooted) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
@@ -6061,7 +6098,7 @@
}
}
if (mBooted) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
@@ -6736,6 +6773,15 @@
}
@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 @@
+ "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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
*/
@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,31 @@
@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);
+ }
+ }
+
+ @Override
+ public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+ Rect tempDockedTaskInsetBounds,
+ Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "resizeDockedStack()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
+ tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
+ PRESERVE_WINDOWS);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9541,8 +9597,7 @@
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
- enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
- "positionTaskInStack()");
+ 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}
@@ -12714,10 +12771,10 @@
// 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 @@
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 @@
@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 @@
@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 @@
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 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 9680382..4101dde 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 @@
// 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 4d9120b..4fb87c3 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 @@
(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 1492e23..8d9cb58 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 @@
// 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 @@
}
}
+ 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 @@
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 @@
// 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 @@
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, "stop-config");
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
mStackSupervisor.updatePreviousProcessLocked(r);
}
@@ -1131,17 +1141,16 @@
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 @@
return null;
}
- private ActivityStack getNextVisibleStackLocked() {
+ ActivityStack getNextFocusableStackLocked() {
ArrayList<ActivityStack> stacks = mStacks;
final ActivityRecord parent = mActivityContainer.mParentActivity;
if (parent != null) {
@@ -1305,7 +1314,7 @@
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 @@
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 @@
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 @@
}
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 @@
setVisible(r, true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, noStackActivityResumed, false);
+ mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
return true;
}
}
@@ -1772,15 +1781,17 @@
*
* @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 @@
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 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 @@
// 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 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 @@
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 @@
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 @@
if (mPausingActivity == r) {
mPausingActivity = null;
}
- mService.clearFocusedActivity(r);
+ mService.resetFocusedActivityIfNeededLocked(r);
r.configDestroy = false;
r.frozenBeforeDestroy = false;
@@ -3526,7 +3527,7 @@
}
}
if (activityRemoved) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
@@ -3700,7 +3701,7 @@
removeActivityFromHistoryLocked(r, reason);
}
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3738,7 +3739,7 @@
setVisibleBehindActivity(null);
mStackSupervisor.scheduleIdleTimeoutLocked(null);
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
boolean hasVisibleBehindActivity() {
@@ -3953,7 +3954,7 @@
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 @@
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 @@
return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack");
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
return true;
}
@@ -4313,6 +4314,7 @@
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 @@
// 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 @@
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 @@
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 @@
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 32671acf..80a75ce 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.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 @@
*/
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 @@
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 @@
private final ActivityMetricsLogger mActivityMetricsLogger;
+ private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
+
static class FindTaskResult {
ActivityRecord r;
boolean matchedByRootAffinity;
@@ -432,6 +435,7 @@
mRecentTasks = recentTasks;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
+ mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler);
}
/**
@@ -492,8 +496,8 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
//mWindowManager.dump();
if (activityRemoved) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
return r;
@@ -1611,35 +1619,17 @@
}
}
- 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;
+ if (targetStack != null && isFocusedStack(targetStack)) {
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- // 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);
- }
- }
- }
- return result;
+ mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+ return false;
}
void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
@@ -1681,10 +1671,13 @@
}
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 @@
"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 @@
}
}
- 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,96 +1825,139 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
-
- 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();
-
- 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);
- }
- }
-
- mTmpConfigs.put(task.taskId, task.mOverrideConfig);
- mTmpBounds.put(task.taskId, task.mBounds);
- }
- 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 */);
- }
-
- // 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);
- }
- }
- }
- }
- // 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);
- }
- }
+ resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
+ ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows);
} finally {
mWindowManager.continueSurfaceLayout();
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
+ 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();
+ 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);
+ if (tempTaskInsetBounds != null) {
+ mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+ }
+ }
+
+ // 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);
+ }
+
+ 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 */);
+ }
+ }
+
+ 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) {
if (!task.mResizeable) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
@@ -1961,7 +2005,7 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
// 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 @@
final ActivityStack stack = stacks.get(stackNdx);
stack.awakeFromSleepingLocked();
if (isFocusedStack(stack)) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
}
@@ -3124,7 +3179,7 @@
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Tasks remaining, can't unlock");
lockedTask.performClearTaskLocked();
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
return;
}
}
@@ -3165,7 +3220,7 @@
if (andResume) {
findTaskToMoveToFrontLocked(task, 0, null, reason);
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
@@ -3233,7 +3288,7 @@
didSomething = true;
}
if (didSomething) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
@@ -3241,6 +3296,14 @@
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 @@
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
} break;
case SLEEP_TIMEOUT_MSG: {
@@ -3661,7 +3724,7 @@
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 b16e160..f2864d4 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
} 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,682 +728,6 @@
}
}
- 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 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;
- }
-
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
- launchFlags |= 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;
- }
- }
-
- // 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;
- 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.
- if (!doResume || !mSupervisor.okToShowLocked(r)) {
- r.delayedResume = true;
- doResume = false;
- }
-
- final ActivityRecord notTop =
- (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : 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) {
- ActivityRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
- notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
- }
- }
-
- boolean addingToTask = false;
- TaskRecord reuseTask = 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();
- if (baseIntent == null) {
- ActivityOptions.abort(options);
- throw new IllegalArgumentException("Launching into task without base intent: "
- + inTask);
- }
-
- // 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);
- throw new IllegalArgumentException("Trying to launch singleInstance/Task "
- + r + " into different task " + inTask);
- }
- if (root != null) {
- ActivityOptions.abort(options);
- throw new IllegalArgumentException("Caller with inTask " + inTask
- + " has root " + root + " but target is singleInstance/Task");
- }
- }
-
- // 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;
-
- } else {
- addingToTask = true;
- }
-
- reuseTask = inTask;
- } 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;
- }
- }
-
- if (inTask == null) {
- if (sourceRecord == 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) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.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) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= 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;
- }
-
- boolean movedHome = false;
- ActivityStack targetStack;
-
- intent.setFlags(launchFlags);
- final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
-
- 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);
- }
-
- 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;
- }
- }
- 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);
- }
- mSupervisor.updateUserStackLocked(r.userId, targetStack);
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- 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");
- }
-
- }
- } 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;
- }
- } 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);
- }
- mSupervisor.updateUserStackLocked(r.userId, targetStack);
- return ActivityManager.START_TASK_TO_FRONT;
- }
- }
-
- //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;
- }
- } 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;
- }
-
- boolean newTask = false;
- boolean keepCurTransition = false;
-
- final TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
- sourceRecord.task : null;
-
- // 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 (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");
- }
- }
- 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;
- }
- }
-
- 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);
-
- if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
- r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
- }
- 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");
- }
- 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);
- }
-
- return ActivityManager.START_SUCCESS;
- }
-
- private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
- boolean launchSingleTask, int launchFlags) {
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
- (launchSingleInstance || launchSingleTask)) {
- // We have a conflict between the Intent and the Activity manifest, manifest wins.
- Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
- "\"singleInstance\" or \"singleTask\"");
- launchFlags &=
- ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
- } else {
- switch (r.info.documentLaunchMode) {
- case ActivityInfo.DOCUMENT_LAUNCH_NONE:
- break;
- case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
- break;
- case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
- break;
- case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
- launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
- break;
- }
- }
- return launchFlags;
- }
-
final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
Bundle bOptions, int userId) {
@@ -1381,7 +801,743 @@
Binder.restoreCallingIdentity(origId);
}
- return ActivityManager.START_SUCCESS;
+ 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 ((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 = (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;
+ mDoResume = false;
+ }
+
+ 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;
+ }
+
+ 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(
+ mNotTop);
+ }
+ if (!checkedCaller.realActivity.equals(r.realActivity)) {
+ // Caller is not the same as launcher, so always needed.
+ mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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(mOptions);
+ throw new IllegalArgumentException("Launching into task without base intent: "
+ + 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 (mLaunchSingleInstance || mLaunchSingleTask) {
+ if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
+ ActivityOptions.abort(mOptions);
+ throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+ + mStartActivity + " into different task " + mInTask);
+ }
+ if (root != null) {
+ ActivityOptions.abort(mOptions);
+ throw new IllegalArgumentException("Caller with mInTask " + mInTask
+ + " has root " + root + " but target is singleInstance/Task");
+ }
+ }
+
+ // 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
+ | 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 {
+ mAddingToTask = true;
+ }
+
+ mReuseTask = mInTask;
+ } else {
+ 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 (mInTask == null) {
+ if (mSourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ 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: " + mIntent);
+ mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+ }
+ } 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.
+ 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.
+ mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+ }
+ }
+ }
+
+ private void computeSourceStack() {
+ if (mSourceRecord == null) {
+ mSourceStack = null;
+ return;
+ }
+ if (!mSourceRecord.finishing) {
+ mSourceStack = mSourceRecord.task.stack;
+ return;
+ }
+
+ // 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ 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);
+ }
+ 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);
+ }
+ mOptions = null;
+ }
+ }
+ 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);
+ }
+ 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 (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);
+ }
+ 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);
+ }
+ }
+
+ 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 {
+ ActivityOptions.abort(mOptions);
+ }
+ mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+ }
+
+ private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
+ mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags,
+ mOptions);
+ if (mDoResume) {
+ mTargetStack.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);
+ }
+ }
+
+ 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;
+ }
+ } 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;
+ }
+ }
+
+ // 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;
+ }
+
+ 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");
+ }
+ }
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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,
+ boolean launchSingleTask, int launchFlags) {
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+ (launchSingleInstance || launchSingleTask)) {
+ // We have a conflict between the Intent and the Activity manifest, manifest wins.
+ Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
+ "\"singleInstance\" or \"singleTask\"");
+ launchFlags &=
+ ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+ } else {
+ switch (r.info.documentLaunchMode) {
+ case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+ launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+ break;
+ }
+ }
+ return launchFlags;
}
final void doPendingActivityLaunchesLocked(boolean doResume) {
@@ -1389,9 +1545,8 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
- /**
- * 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 0000000..ff39589
--- /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 4647d77..c97d09c 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.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 @@
} 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 @@
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 @@
}
}
- 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 20b1e60..f2c5206 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 @@
+ 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 @@
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 c5e6e7c..4eabe36 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 @@
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 @@
}
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 @@
@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 @@
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 @@
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 1987214..ce18818 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 @@
}
@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 b7662da..09e6647 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 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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 @@
*/
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 @@
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 49aa73e..8657049 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.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.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.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 @@
// 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 @@
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 @@
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
+ mAssistant.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
@@ -779,6 +788,7 @@
// 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 @@
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 @@
importOldBlockDb();
mListeners = new NotificationListeners();
+ mAssistant = new NotificationAssistant();
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -1025,6 +1037,7 @@
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
+ mAssistant.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1896,6 +1909,22 @@
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 @@
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 @@
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 @@
}
}
- private void handleRankingConfigChange() {
+ private void handleRankingSort() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
@@ -2714,7 +2750,8 @@
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 @@
}
- 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 @@
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 @@
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 @@
}
@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 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
similarity index 77%
rename from core/java/android/service/notification/NotificationAdjustment.aidl
rename to services/core/java/com/android/server/notification/RankingHandler.java
index 805fe2c..80bb4f0 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 3287f67..0662e7a 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.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.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 @@
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 @@
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 @@
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 85c3cf8..276c6ba 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 boolean shouldSuppressScreenOn() {
+ synchronized (mConfig) {
+ return !mConfig.allowScreenOn;
+ }
+ }
+
public void addCallback(Callback callback) {
mCallbacks.add(callback);
}
@@ -435,11 +441,12 @@
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 7bb6d1d..7e186a0 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.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.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 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 @@
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 @@
@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 @@
@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 @@
@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 @@
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 @@
@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 @@
}
/**
- * 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 @@
@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 @@
@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 @@
@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 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 @@
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 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 @@
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 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 @@
@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 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 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
}
@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 @@
try {
pp.collectCertificates(pkg, parseFlags);
- pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -10037,7 +10079,7 @@
}
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 @@
+ " 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 @@
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 @@
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 @@
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 @@
/** 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 @@
/** 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 @@
/** 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 @@
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 @@
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 @@
/** 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 @@
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 d2a3ede..b330139 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 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 6fcf1d6..72611b7 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 @@
if (awakenFromDreams) {
awakenDreams();
}
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
hideRecentApps(false, true);
} else {
// Otherwise, just launch Home
@@ -4243,6 +4242,7 @@
&& (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 29e3c63..549d2dc 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 @@
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 b49641f..573aaec 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 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.WindowManager;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
class AppTokenList extends ArrayList<AppWindowToken> {
@@ -123,6 +126,10 @@
// 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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 8f3d3e3..7295318 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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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);
+ }
}
- mListener = listener;
- reevaluateVisibility(true);
+ 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);
+ }
+ }
+ 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 27e7a31..223e03a 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 b961879..67debe6 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 @@
* @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 @@
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 @@
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 263b411..2cf2618 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 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 @@
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 @@
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 @@
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 @@
// 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 6385caa..d722ec7 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.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.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.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 @@
}
}
- 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 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 @@
(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 @@
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 @@
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 @@
* @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 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 @@
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 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 @@
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 @@
}
}
+ @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 @@
}
@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 733bc29..6e4e01f 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 @@
// 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 @@
// 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 @@
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 @@
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));
+ 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, mFrame.left),
- Math.max(mVisibleFrame.top, mFrame.top),
- Math.min(mVisibleFrame.right, mFrame.right),
- Math.min(mVisibleFrame.bottom, mFrame.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, mFrame.left),
- Math.max(mStableFrame.top, mFrame.top),
- Math.min(mStableFrame.right, mFrame.right),
- Math.min(mStableFrame.bottom, mFrame.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 - mFrame.left, 0),
- Math.max(mOverscanFrame.top - mFrame.top, 0),
- Math.max(mFrame.right - mOverscanFrame.right, 0),
- Math.max(mFrame.bottom - mOverscanFrame.bottom, 0));
+ 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 - mFrame.left,
- mContentFrame.top - mFrame.top,
- mFrame.right - mContentFrame.right,
- mFrame.bottom - mContentFrame.bottom);
+ mContentInsets.set(mContentFrame.left - frame.left,
+ mContentFrame.top - frame.top,
+ frame.right - mContentFrame.right,
+ frame.bottom - mContentFrame.bottom);
- mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
- mVisibleFrame.top - mFrame.top,
- mFrame.right - mVisibleFrame.right,
- mFrame.bottom - mVisibleFrame.bottom);
+ mVisibleInsets.set(mVisibleFrame.left - frame.left,
+ mVisibleFrame.top - frame.top,
+ frame.right - mVisibleFrame.right,
+ frame.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));
+ 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 @@
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 @@
// 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 @@
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 @@
}
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 381db56..83ab190 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_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 @@
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 6ebda07..feeab27 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 @@
}
}
}
- /*
- * 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 0bbd23e..dd6493c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1322,7 +1322,7 @@
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 df7b412..2640889 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 @@
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 5d5df95..aa95e1d 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -242,8 +242,13 @@
*/
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 27ee804..a915d37 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -155,8 +155,7 @@
// 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 @@
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 5087080..426b240 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -49,7 +49,7 @@
* <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 8537f9c..c680999 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -153,6 +153,17 @@
* @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 @@
}
/** @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 2813df5..2cef1b3 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.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 @@
@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 @@
@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 3c2659f..b78fd49 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 @@
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 40437fa..01ee18b 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -76,7 +76,7 @@
@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 @@
}
@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 683c4aa..c8e3d03 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 @@
*/
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 a2fad13..3f9574f 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.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 @@
@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 @@
@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 9c89bfe..dfbc69b 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 @@
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 @@
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 @@
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 0000000..0435280
--- /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);
+ }
+}