Merge "Temporary hide notifications when folding/unfolding" into main
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 8494326..4a0d57f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -536,7 +536,7 @@
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
String callingPackage, int callingUid) throws IOException {
final RevocableFileDescriptor revocableFd =
- new RevocableFileDescriptor(mContext, fd);
+ new RevocableFileDescriptor(mContext, fd, BlobStoreUtils.getRevocableFdHandler());
final Accessor accessor;
synchronized (mRevocableFds) {
accessor = new Accessor(callingPackage, callingUid);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 8eef8ce..ede29ec 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -223,7 +223,8 @@
FileDescriptor fd = null;
try {
fd = openWriteInternal(offsetBytes, lengthBytes);
- final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd,
+ BlobStoreUtils.getRevocableFdHandler());
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
IoUtils.closeQuietly(fd);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 8f94273..a4eae014b 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UserHandle;
import android.text.format.TimeMigrationUtils;
import android.util.Slog;
@@ -63,4 +65,27 @@
static String formatTime(long timeMs) {
return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs);
}
+
+ private static Handler sRevocableFdHandler;
+ private static final Object sLock = new Object();
+
+ // By default, when using a RevocableFileDescriptor, callbacks will be sent to the process'
+ // main looper. In this case that would be system_server's main looper, which is a heavily
+ // contended thread. It can also cause deadlocks, because the volume daemon 'vold' holds a lock
+ // while making these callbacks to the system_server, while at the same time the system_server
+ // main thread can make a call into vold, which requires that same vold lock. To avoid these
+ // issues, use a separate thread for the RevocableFileDescriptor's requests, so that it can
+ // make progress independently of system_server.
+ static @NonNull Handler getRevocableFdHandler() {
+ synchronized (sLock) {
+ if (sRevocableFdHandler != null) {
+ return sRevocableFdHandler;
+ }
+ final HandlerThread t = new HandlerThread("BlobFuseLooper");
+ t.start();
+ sRevocableFdHandler = new Handler(t.getLooper());
+
+ return sRevocableFdHandler;
+ }
+ }
}
diff --git a/core/api/current.txt b/core/api/current.txt
index d75aafb..7e14ae1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6952,6 +6952,8 @@
field public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED";
field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+ field @FlaggedApi("android.app.modes_api") public static final int AUTOMATIC_RULE_STATUS_ACTIVATED = 4; // 0x4
+ field @FlaggedApi("android.app.modes_api") public static final int AUTOMATIC_RULE_STATUS_DEACTIVATED = 5; // 0x5
field public static final int AUTOMATIC_RULE_STATUS_DISABLED = 2; // 0x2
field public static final int AUTOMATIC_RULE_STATUS_ENABLED = 1; // 0x1
field public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3; // 0x3
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b8bea9d..b0332c3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -272,6 +273,7 @@
* {@link #AUTOMATIC_RULE_STATUS_UNKNOWN}.
* </p>
*/
+ // TODO (b/309101513): Add new status types to javadoc
public static final String EXTRA_AUTOMATIC_ZEN_RULE_STATUS =
"android.app.extra.AUTOMATIC_ZEN_RULE_STATUS";
@@ -286,7 +288,8 @@
/** @hide */
@IntDef(prefix = { "AUTOMATIC_RULE_STATUS" }, value = {
AUTOMATIC_RULE_STATUS_ENABLED, AUTOMATIC_RULE_STATUS_DISABLED,
- AUTOMATIC_RULE_STATUS_REMOVED, AUTOMATIC_RULE_STATUS_UNKNOWN
+ AUTOMATIC_RULE_STATUS_REMOVED, AUTOMATIC_RULE_STATUS_UNKNOWN,
+ AUTOMATIC_RULE_STATUS_ACTIVATED, AUTOMATIC_RULE_STATUS_DEACTIVATED
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutomaticZenRuleStatus {}
@@ -320,6 +323,27 @@
public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3;
/**
+ * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule has been
+ * activated by the user or cross device sync. Sent from
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. If the rule owner has a mode that includes
+ * a DND component, the rule owner should activate any extra behavior that's part of that mode
+ * in response to this broadcast.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int AUTOMATIC_RULE_STATUS_ACTIVATED = 4;
+
+ /**
+ * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule has been
+ * deactivated ("snoozed") by the user. The rule will not return to an activated state until
+ * the app calls {@link #setAutomaticZenRuleState(String, Condition)} with
+ * {@link Condition#STATE_FALSE} (either immediately or when the trigger criteria is no
+ * longer met) and then {@link Condition#STATE_TRUE} when the trigger criteria is freshly met,
+ * or when the user re-activates it.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int AUTOMATIC_RULE_STATUS_DEACTIVATED = 5;
+
+ /**
* Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes.
*
* <p>This broadcast is only sent to registered receivers and (starting from
diff --git a/core/java/android/os/RevocableFileDescriptor.java b/core/java/android/os/RevocableFileDescriptor.java
index ac2cd60..7093c49 100644
--- a/core/java/android/os/RevocableFileDescriptor.java
+++ b/core/java/android/os/RevocableFileDescriptor.java
@@ -73,11 +73,26 @@
init(context, fd);
}
+ public RevocableFileDescriptor(Context context, FileDescriptor fd, Handler handler)
+ throws IOException {
+ init(context, fd, handler);
+ }
+
/** {@hide} */
public void init(Context context, FileDescriptor fd) throws IOException {
+ init(context, fd, null);
+ }
+
+ /** {@hide} */
+ public void init(Context context, FileDescriptor fd, Handler handler) throws IOException {
mInner = fd;
- mOuter = context.getSystemService(StorageManager.class)
- .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ StorageManager sm = context.getSystemService(StorageManager.class);
+ if (handler != null) {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback,
+ handler);
+ } else {
+ mOuter = sm.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_WRITE, mCallback);
+ }
}
/**
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 576ebc1..65e1605 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -74,6 +74,14 @@
controller = std::shared_ptr<PointerController>(
new MousePointerController(policy, looper, spriteController, enabled));
break;
+ case ControllerType::TOUCH:
+ controller = std::shared_ptr<PointerController>(
+ new TouchPointerController(policy, looper, spriteController, enabled));
+ break;
+ case ControllerType::STYLUS:
+ controller = std::shared_ptr<PointerController>(
+ new StylusPointerController(policy, looper, spriteController, enabled));
+ break;
case ControllerType::LEGACY:
default:
controller = std::shared_ptr<PointerController>(
@@ -167,8 +175,7 @@
FloatPoint PointerController::getPosition() const {
if (!mEnabled) {
- return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ return FloatPoint{0, 0};
}
const int32_t displayId = mCursorController.getDisplayId();
@@ -397,4 +404,34 @@
PointerController::setPresentation(Presentation::POINTER);
}
+MousePointerController::~MousePointerController() {
+ MousePointerController::fade(Transition::IMMEDIATE);
+}
+
+// --- TouchPointerController ---
+
+TouchPointerController::TouchPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ SpriteController& spriteController, bool enabled)
+ : PointerController(policy, looper, spriteController, enabled) {
+ PointerController::setPresentation(Presentation::SPOT);
+}
+
+TouchPointerController::~TouchPointerController() {
+ TouchPointerController::clearSpots();
+}
+
+// --- StylusPointerController ---
+
+StylusPointerController::StylusPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ SpriteController& spriteController, bool enabled)
+ : PointerController(policy, looper, spriteController, enabled) {
+ PointerController::setPresentation(Presentation::STYLUS_HOVER);
+}
+
+StylusPointerController::~StylusPointerController() {
+ StylusPointerController::fade(Transition::IMMEDIATE);
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 08e19a0..fa07c39 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -68,7 +68,7 @@
void updatePointerIcon(PointerIconStyle iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setInactivityTimeout(InactivityTimeout inactivityTimeout);
+ virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
void reloadPointerResources();
void onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports);
@@ -143,6 +143,74 @@
const sp<Looper>& looper, SpriteController& spriteController,
bool enabled);
+ ~MousePointerController() override;
+
+ void setPresentation(Presentation) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32, int32_t) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void clearSpots() override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+};
+
+class TouchPointerController : public PointerController {
+public:
+ /** A version of PointerController that controls touch spots. */
+ TouchPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled);
+
+ ~TouchPointerController() override;
+
+ std::optional<FloatRect> getBounds() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void move(float, float) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setPosition(float, float) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ FloatPoint getPosition() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ int32_t getDisplayId() const override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void fade(Transition) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void unfade(Transition) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setDisplayViewport(const DisplayViewport&) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setPresentation(Presentation) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void updatePointerIcon(PointerIconStyle) {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setCustomPointerIcon(const SpriteIcon&) {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ // fade() should not be called by inactivity timeout. Do nothing.
+ void setInactivityTimeout(InactivityTimeout) override {}
+};
+
+class StylusPointerController : public PointerController {
+public:
+ /** A version of PointerController that controls one stylus pointer. */
+ StylusPointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled);
+
+ ~StylusPointerController() override;
+
void setPresentation(Presentation) override {
LOG_ALWAYS_FATAL("Should not be called");
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8230a82..bde0c0c 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -759,13 +759,7 @@
return;
}
- synchronized (mLock) {
- mRoutes.clear();
- for (MediaRoute2Info route : currentRoutes) {
- mRoutes.put(route.getId(), route);
- }
- updateFilteredRoutesLocked();
- }
+ updateRoutesOnHandler(currentRoutes);
RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo();
mSystemController.setRoutingSessionInfo(currentSystemSessionInfo);
@@ -824,10 +818,10 @@
}
}
- void updateRoutesOnHandler(List<MediaRoute2Info> routes) {
+ void updateRoutesOnHandler(List<MediaRoute2Info> newRoutes) {
synchronized (mLock) {
mRoutes.clear();
- for (MediaRoute2Info route : routes) {
+ for (MediaRoute2Info route : newRoutes) {
mRoutes.put(route.getId(), route);
}
updateFilteredRoutesLocked();
@@ -2703,16 +2697,6 @@
notifyRouteListingPreferenceUpdated(routeListingPreference);
}
- private void onRoutesUpdatedOnHandler(@NonNull List<MediaRoute2Info> routes) {
- synchronized (mLock) {
- mRoutes.clear();
- for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getId(), route);
- }
- updateFilteredRoutesLocked();
- }
- }
-
private void onRequestFailedOnHandler(int requestId, int reason) {
MediaRouter2Manager.TransferRequest matchingRequest = null;
for (MediaRouter2Manager.TransferRequest request : mTransferRequests) {
@@ -2786,9 +2770,7 @@
public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
mHandler.sendMessage(
obtainMessage(
- ProxyMediaRouter2Impl::onRoutesUpdatedOnHandler,
- ProxyMediaRouter2Impl.this,
- routes));
+ MediaRouter2::updateRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 46a033d..e3f8fbb 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -47,12 +47,14 @@
android:textAppearance="?android:attr/textAppearanceListItem"
style="@style/MainSwitchText.Settingslib" />
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 6d1e76c..255b2c9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -49,12 +49,14 @@
android:lineBreakWordStyle="phrase"
style="@style/MainSwitchText.Settingslib" />
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index 5be0093..bf34db9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -38,11 +38,13 @@
android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
android:textAlignment="viewStart"/>
- <include
+ <Switch
android:id="@android:id/switch_widget"
- layout="@layout/preference_widget_switch_compat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/SwitchBar.Switch.Settingslib"/>
</LinearLayout>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 5447f21..1c92696 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -57,15 +57,18 @@
private fun UserManager.getUserGroups(): List<UserGroup> {
val userGroupList = mutableListOf<UserGroup>()
- val profileToShowInSettingsList = getProfiles(UserHandle.myUserId())
- .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle).showInSettings }
+ val profileToShowInSettings = getProfiles(UserHandle.myUserId())
+ .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle) }
- profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
+ profileToShowInSettings
+ .filter { it.second.showInSettings == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
.takeIf { it.isNotEmpty() }
?.map { it.first }
?.let { userInfos -> userGroupList += UserGroup(userInfos) }
- profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_LAUNCHER_SEPARATE }
+ profileToShowInSettings
+ .filter { it.second.showInSettings == UserProperties.SHOW_IN_SETTINGS_SEPARATE &&
+ (!it.second.hideInSettingsInQuietMode || !it.first.isQuietModeEnabled) }
.forEach { userGroupList += UserGroup(userInfos = listOf(it.first)) }
return userGroupList
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 03d1cda..e77964c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -243,6 +243,32 @@
}
/**
+ * Checks whether add user is disabled on the device
+ *
+ * @param context {@link Context} for the calling user.
+ *
+ *
+ * @param userId User to check enforced admin status for.
+ *
+ * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+ * or {@code null} If adding user is not disabled.
+ */
+ public static EnforcedAdmin checkIfAddUserDisallowed(Context context, int userId) {
+ final UserManager um = UserManager.get(context);
+ if (!um.hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.of(userId))) {
+ // Restriction is not enforced.
+ return null;
+ }
+ EnforcedAdmin enforcedAdmin = checkIfRestrictionEnforced(context,
+ UserManager.DISALLOW_ADD_USER, userId);
+ if (enforcedAdmin != null) {
+ return enforcedAdmin;
+ }
+ return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(
+ UserManager.DISALLOW_ADD_USER);
+ }
+
+ /**
* Check if an application is suspended.
*
* @return EnforcedAdmin Object containing the enforced admin component and admin user details,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index eb10afc..aae61bd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -33,7 +33,9 @@
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.geometry.lerp
+import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.IntermediateMeasureScope
import androidx.compose.ui.layout.Measurable
@@ -85,6 +87,9 @@
/** The size of this element. */
var size = SizeUnspecified
+ /** The draw scale of this element. */
+ var drawScale = Scale.Default
+
/** The alpha of this element. */
var alpha = AlphaUnspecified
}
@@ -110,6 +115,13 @@
}
}
+data class Scale(val scaleX: Float, val scaleY: Float, val pivot: Offset = Offset.Unspecified) {
+
+ companion object {
+ val Default = Scale(1f, 1f, Offset.Unspecified)
+ }
+}
+
/** The implementation of [SceneScope.element]. */
@OptIn(ExperimentalComposeUiApi::class)
internal fun Modifier.element(
@@ -160,9 +172,24 @@
}
}
+ val drawScale by
+ remember(layoutImpl, element, scene, sceneValues) {
+ derivedStateOf { getDrawScale(layoutImpl, element, scene, sceneValues) }
+ }
+
drawWithContent {
if (shouldDrawElement(layoutImpl, scene, element)) {
- drawContent()
+ if (drawScale == Scale.Default) {
+ this@drawWithContent.drawContent()
+ } else {
+ scale(
+ drawScale.scaleX,
+ drawScale.scaleY,
+ if (drawScale.pivot.isUnspecified) center else drawScale.pivot
+ ) {
+ this@drawWithContent.drawContent()
+ }
+ }
}
}
.modifierTransformations(layoutImpl, scene, element, sceneValues)
@@ -377,6 +404,28 @@
return placeable
}
+private fun getDrawScale(
+ layoutImpl: SceneTransitionLayoutImpl,
+ element: Element,
+ scene: Scene,
+ sceneValues: Element.TargetValues
+): Scale {
+ return computeValue(
+ layoutImpl,
+ scene,
+ element,
+ sceneValue = { Scale.Default },
+ transformation = { it.drawScale },
+ idleValue = Scale.Default,
+ currentValue = { Scale.Default },
+ lastValue = {
+ sceneValues.lastValues.drawScale.takeIf { it != Scale.Default }
+ ?: element.lastSharedValues.drawScale
+ },
+ ::lerp,
+ )
+}
+
@OptIn(ExperimentalComposeUiApi::class)
private fun IntermediateMeasureScope.place(
layoutImpl: SceneTransitionLayoutImpl,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index b163a2a..72a2d61 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.util.fastMap
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
+import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.ModifierTransformation
@@ -126,6 +127,7 @@
val modifier = mutableListOf<ModifierTransformation>()
var offset: PropertyTransformation<Offset>? = null
var size: PropertyTransformation<IntSize>? = null
+ var drawScale: PropertyTransformation<Scale>? = null
var alpha: PropertyTransformation<Float>? = null
fun <T> onPropertyTransformation(
@@ -144,6 +146,10 @@
throwIfNotNull(size, element, name = "size")
size = root as PropertyTransformation<IntSize>
}
+ is DrawScale -> {
+ throwIfNotNull(drawScale, element, name = "drawScale")
+ drawScale = root as PropertyTransformation<Scale>
+ }
is Fade -> {
throwIfNotNull(alpha, element, name = "alpha")
alpha = root as PropertyTransformation<Float>
@@ -167,7 +173,7 @@
}
}
- return ElementTransformations(shared, modifier, offset, size, alpha)
+ return ElementTransformations(shared, modifier, offset, size, drawScale, alpha)
}
private fun throwIfNotNull(
@@ -187,5 +193,6 @@
val modifier: List<ModifierTransformation>,
val offset: PropertyTransformation<Offset>?,
val size: PropertyTransformation<IntSize>?,
+ val drawScale: PropertyTransformation<Scale>?,
val alpha: PropertyTransformation<Float>?,
)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 7b7ddfa..ca66dff5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene
import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
@@ -224,12 +225,22 @@
/**
* Scale the [width] and [height] of the element(s) matching [matcher]. Note that this scaling
* is done during layout, so it will potentially impact the size and position of other elements.
- *
- * TODO(b/290184746): Also provide a scaleDrawing() to scale an element at drawing time.
*/
fun scaleSize(matcher: ElementMatcher, width: Float = 1f, height: Float = 1f)
/**
+ * Scale the drawing with [scaleX] and [scaleY] of the element(s) matching [matcher]. Note this
+ * will only scale the draw inside of an element, therefore it won't impact layout of elements
+ * around it.
+ */
+ fun scaleDraw(
+ matcher: ElementMatcher,
+ scaleX: Float = 1f,
+ scaleY: Float = 1f,
+ pivot: Offset = Offset.Unspecified
+ )
+
+ /**
* Scale the element(s) matching [matcher] so that it grows/shrinks to the same size as [anchor]
* .
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index d2bfd91..d490989 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -21,10 +21,12 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.spring
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
+import com.android.compose.animation.scene.transformation.DrawScale
import com.android.compose.animation.scene.transformation.EdgeTranslate
import com.android.compose.animation.scene.transformation.Fade
import com.android.compose.animation.scene.transformation.PropertyTransformation
@@ -178,6 +180,10 @@
transformation(ScaleSize(matcher, width, height))
}
+ override fun scaleDraw(matcher: ElementMatcher, scaleX: Float, scaleY: Float, pivot: Offset) {
+ transformation(DrawScale(matcher, scaleX, scaleY, pivot))
+ }
+
override fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey) {
transformation(AnchoredSize(matcher, anchor))
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
new file mode 100644
index 0000000..d1cf8ee
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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.compose.animation.scene.transformation
+
+import androidx.compose.ui.geometry.Offset
+import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementMatcher
+import com.android.compose.animation.scene.Scale
+import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.TransitionState
+
+/**
+ * Scales the draw size of an element. Note this will only scale the draw inside of an element,
+ * therefore it won't impact layout of elements around it.
+ */
+internal class DrawScale(
+ override val matcher: ElementMatcher,
+ private val scaleX: Float,
+ private val scaleY: Float,
+ private val pivot: Offset = Offset.Unspecified,
+) : PropertyTransformation<Scale> {
+
+ override fun transform(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: Scene,
+ element: Element,
+ sceneValues: Element.TargetValues,
+ transition: TransitionState.Transition,
+ value: Scale,
+ ): Scale {
+ return Scale(scaleX, scaleY, pivot)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
index 62d67f0..984086b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
@@ -19,9 +19,11 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
@@ -30,6 +32,7 @@
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.withSaveLayer
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.toSize
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
@@ -43,6 +46,11 @@
private val bounds: ElementKey,
private val shape: Shape,
) : ModifierTransformation {
+
+ private var lastSize: Size = Size.Unspecified
+ private var lastLayoutDirection: LayoutDirection = LayoutDirection.Ltr
+ private var lastOutline: Outline? = null
+
override fun Modifier.transform(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
@@ -59,7 +67,6 @@
drawContent()
return@drawWithContent
}
-
drawIntoCanvas { canvas ->
canvas.withSaveLayer(size.toRect(), Paint()) {
drawContent()
@@ -78,13 +85,19 @@
return
}
- // TODO(b/290184746): Cache outline if the size of bounds does not change.
+ val outline =
+ if (boundsSize == lastSize && layoutDirection == lastLayoutDirection) {
+ lastOutline!!
+ } else {
+ val newOutline = shape.createOutline(boundsSize, layoutDirection, this)
+ lastSize = boundsSize
+ lastLayoutDirection = layoutDirection
+ lastOutline = newOutline
+ newOutline
+ }
+
drawOutline(
- shape.createOutline(
- boundsSize,
- layoutDirection,
- this,
- ),
+ outline,
Color.Black,
blendMode = BlendMode.DstOut,
)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
index eb1a634..13747b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
@@ -17,7 +17,10 @@
package com.android.compose.ui.util
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.Scale
import kotlin.math.roundToInt
import kotlin.math.roundToLong
@@ -43,3 +46,19 @@
lerp(start.height, stop.height, fraction)
)
}
+
+/** Linearly interpolate between [start] and [stop] with [fraction] fraction between them. */
+fun lerp(start: Scale, stop: Scale, fraction: Float): Scale {
+ val pivot =
+ when {
+ start.pivot.isSpecified && stop.pivot.isSpecified ->
+ lerp(start.pivot, stop.pivot, fraction)
+ start.pivot.isSpecified -> start.pivot
+ else -> stop.pivot
+ }
+ return Scale(
+ lerp(start.scaleX, stop.scaleX, fraction),
+ lerp(start.scaleY, stop.scaleY, fraction),
+ pivot
+ )
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
new file mode 100644
index 0000000..3ec73c7
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/ui/util/MathHelpersTest.kt
@@ -0,0 +1,56 @@
+package com.android.compose.ui.util
+
+import androidx.compose.ui.geometry.Offset
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Scale
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MathHelpersTest {
+
+ @Test
+ fun lerpScaleWithPivotUnspecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f)
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithFirstPivotSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f)
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithSecondPivotSpecified() {
+ val scale1 = Scale(1f, 1f)
+ val scale2 = Scale(5f, 3f, Offset(1f, 1f))
+ val expectedScale = Scale(3f, 2f, Offset(1f, 1f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+
+ @Test
+ fun lerpScaleWithBothPivotsSpecified() {
+ val scale1 = Scale(1f, 1f, Offset(1f, 1f))
+ val scale2 = Scale(5f, 3f, Offset(3f, 5f))
+ val expectedScale = Scale(3f, 2f, Offset(2f, 3f))
+
+ val actualScale = lerp(scale1, scale2, 0.5f)
+
+ assertThat(actualScale).isEqualTo(expectedScale)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index c924df6..3492365 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -45,7 +45,7 @@
/** See [registerCriticalDumpable]. */
fun registerCriticalDumpable(module: Dumpable) {
- registerCriticalDumpable(module::class.java.simpleName, module)
+ registerCriticalDumpable(module::class.java.canonicalName, module)
}
/**
@@ -62,7 +62,7 @@
/** See [registerNormalDumpable]. */
fun registerNormalDumpable(module: Dumpable) {
- registerNormalDumpable(module::class.java.simpleName, module)
+ registerNormalDumpable(module::class.java.canonicalName, module)
}
/**
@@ -105,12 +105,12 @@
}
/**
- * Same as the above override, but automatically uses the simple class name as the dumpable
+ * Same as the above override, but automatically uses the canonical class name as the dumpable
* name.
*/
@Synchronized
fun registerDumpable(module: Dumpable) {
- registerDumpable(module::class.java.simpleName, module)
+ registerDumpable(module::class.java.canonicalName, module)
}
/** Unregisters a previously-registered dumpable. */
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9802adf..762c1a1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -16,14 +16,19 @@
package com.android.server.notification;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
import android.app.Flags;
@@ -31,6 +36,9 @@
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -51,6 +59,7 @@
import android.media.VolumePolicy;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -109,6 +118,13 @@
private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
static final int RULE_LIMIT_PER_PACKAGE = 100;
+ /**
+ * Send new activation AutomaticZenRule statuses to apps with a min target SDK version
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L;
+
// pkg|userId => uid
@VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
@@ -229,7 +245,8 @@
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
updateConfigAndZenModeLocked(mConfig, "init", true /*setRingerMode*/,
- Process.SYSTEM_UID /* callingUid */, true /* is system */);
+ Process.SYSTEM_UID /* callingUid */, true /* is system */,
+ false /* no broadcasts*/);
}
}
@@ -407,10 +424,13 @@
"Cannot update rules not owned by your condition provider");
}
}
- if (rule.enabled != automaticZenRule.isEnabled()) {
- dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.getPkg(), ruleId,
- automaticZenRule.isEnabled()
- ? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED);
+ if (!Flags.modesApi()) {
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.getPkg(), ruleId,
+ automaticZenRule.isEnabled()
+ ? AUTOMATIC_RULE_STATUS_ENABLED
+ : AUTOMATIC_RULE_STATUS_DISABLED);
+ }
}
populateZenRule(rule.pkg, automaticZenRule, rule, false);
@@ -651,6 +671,9 @@
private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
boolean isNew) {
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ rule.snoozing = false;
+ }
rule.name = automaticZenRule.getName();
rule.condition = null;
rule.conditionId = automaticZenRule.getConditionId();
@@ -668,9 +691,6 @@
rule.pkg = pkg;
}
- if (rule.enabled != automaticZenRule.isEnabled()) {
- rule.snoozing = false;
- }
if (Flags.modesApi()) {
rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
rule.iconResId = automaticZenRule.getIconResId();
@@ -706,6 +726,27 @@
return azr;
}
+ @SuppressLint("MissingPermission")
+ void scheduleActivationBroadcast(String pkg, @UserIdInt int userId, String ruleId,
+ boolean activated) {
+ if (CompatChanges.isChangeEnabled(
+ SEND_ACTIVATION_AZR_STATUSES, pkg, UserHandle.of(userId))) {
+ dispatchOnAutomaticRuleStatusChanged(userId, pkg, ruleId, activated
+ ? AUTOMATIC_RULE_STATUS_ACTIVATED
+ : AUTOMATIC_RULE_STATUS_DEACTIVATED);
+ } else {
+ dispatchOnAutomaticRuleStatusChanged(
+ userId, pkg, ruleId, AUTOMATIC_RULE_STATUS_UNKNOWN);
+ }
+ }
+
+ void scheduleEnabledBroadcast(String pkg, @UserIdInt int userId, String ruleId,
+ boolean enabled) {
+ dispatchOnAutomaticRuleStatusChanged(userId, pkg, ruleId, enabled
+ ? AUTOMATIC_RULE_STATUS_ENABLED
+ : AUTOMATIC_RULE_STATUS_DISABLED);
+ }
+
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/, callingUid,
@@ -1002,7 +1043,7 @@
dispatchOnPolicyChanged();
}
updateConfigAndZenModeLocked(config, reason, setRingerMode, callingUid,
- fromSystemOrSystemUi);
+ fromSystemOrSystemUi, true);
mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
return true;
} catch (SecurityException e) {
@@ -1019,13 +1060,31 @@
*/
@GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
- boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
+ boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi,
+ boolean sendBroadcasts) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS);
// Store (a copy of) all config and zen mode info prior to any changes taking effect
ZenModeEventLogger.ZenModeInfo prevInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
if (!config.equals(mConfig)) {
+ // schedule broadcasts
+ if (Flags.modesApi() && sendBroadcasts) {
+ for (ZenRule rule : config.automaticRules.values()) {
+ ZenRule original = mConfig.automaticRules.get(rule.id);
+ if (original != null) {
+ if (original.enabled != rule.enabled) {
+ scheduleEnabledBroadcast(
+ rule.getPkg(), config.user, rule.id, rule.enabled);
+ }
+ if (original.isAutomaticActive() != rule.isAutomaticActive()) {
+ scheduleActivationBroadcast(
+ rule.getPkg(), config.user, rule.id, rule.isAutomaticActive());
+ }
+ }
+ }
+ }
+
mConfig = config;
dispatchOnConfigChanged();
updateConsolidatedPolicy(reason);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a7d7730..2182093 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1293,6 +1293,11 @@
}
void NativeInputManager::setShowTouches(bool enabled) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setShowTouchesEnabled(enabled);
+ return;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -1744,6 +1749,11 @@
}
void NativeInputManager::setStylusPointerIconEnabled(bool enabled) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setStylusPointerIconEnabled(enabled);
+ return;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 0349ad9..e8201fd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,6 +17,10 @@
package com.android.server.notification;
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
@@ -141,6 +145,8 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@@ -2694,6 +2700,221 @@
assertEquals(TRIGGER_DESC, actual.getTriggerDescription());
}
+ @Test
+ public void testUpdateAutomaticRule_disabled_triggersBroadcast() throws Exception {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ zenRule.setEnabled(false);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_enabled_triggersBroadcast() throws Exception {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ zenRule.setEnabled(true);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[1];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[0] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[2];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ int i = 0;
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[i++] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ final int[] actualStatus = new int[2];
+ ZenModeHelper.Callback callback = new ZenModeHelper.Callback() {
+ int i = 0;
+ @Override
+ void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {
+ if (Objects.equals(createdId, id)) {
+ actualStatus[i++] = status;
+ latch.countDown();
+ }
+ }
+ };
+ mZenModeHelper.addCallback(callback);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_FALSE),
+ Process.SYSTEM_UID, true);
+
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
+ }
+
+ @Test
+ public void testUpdateAutomaticRule_unsnoozes() throws IllegalArgumentException {
+ setupZenConfig();
+
+ // Add a new automatic zen rule that's enabled
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ zenRule, "test", Process.SYSTEM_UID, true);
+
+ // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
+ mZenModeHelper.setAutomaticZenRuleState(createdId,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // Event 2: Snooze rule by turning off DND
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ // Event 3: "User" turns off the automatic rule (sets it to not enabled)
+ zenRule.setEnabled(false);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+
+ assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
+ }
+
private void setupZenConfig() {
mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;