diff options
| author | 2023-11-10 04:38:23 +0000 | |
|---|---|---|
| committer | 2023-11-16 15:45:12 +0000 | |
| commit | 3e655086829e19b881e1d9255d405efe814af100 (patch) | |
| tree | de49917ca59712b5d0e17e02654781e61e50502c | |
| parent | 718e462812cdacd3e58f518679894cbff4682f6e (diff) | |
Adds source to Notification Condition
Source specifies the source of, or reason for, the state change
represented by this Condition.
Bug: 308673204
Test: Add new unit test
Change-Id: I6551a5e71c4842fb6ce5602106d70e39bb942e7c
| -rw-r--r-- | core/api/current.txt | 8 | ||||
| -rw-r--r-- | core/java/android/service/notification/Condition.java | 104 | ||||
| -rw-r--r-- | core/java/android/service/notification/ZenModeConfig.java | 11 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/service/notification/ConditionTest.java | 100 |
4 files changed, 213 insertions, 10 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 55933d434e5d..65bfff54d072 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -40527,19 +40527,26 @@ package android.service.notification { public final class Condition implements android.os.Parcelable { ctor public Condition(android.net.Uri, String, int); + ctor @FlaggedApi("android.app.modes_api") public Condition(@Nullable android.net.Uri, @Nullable String, int, int); ctor public Condition(android.net.Uri, String, String, String, int, int, int); + ctor @FlaggedApi("android.app.modes_api") public Condition(@Nullable android.net.Uri, @Nullable String, @Nullable String, @Nullable String, int, int, int, int); ctor public Condition(android.os.Parcel); method public android.service.notification.Condition copy(); method public int describeContents(); method public static boolean isValidId(android.net.Uri, String); method public static android.net.Uri.Builder newId(android.content.Context); method public static String relevanceToString(int); + method @FlaggedApi("android.app.modes_api") @NonNull public static String sourceToString(int); method public static String stateToString(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Condition> CREATOR; field public static final int FLAG_RELEVANT_ALWAYS = 2; // 0x2 field public static final int FLAG_RELEVANT_NOW = 1; // 0x1 field public static final String SCHEME = "condition"; + field @FlaggedApi("android.app.modes_api") public static final int SOURCE_CONTEXT = 3; // 0x3 + field @FlaggedApi("android.app.modes_api") public static final int SOURCE_SCHEDULE = 2; // 0x2 + field @FlaggedApi("android.app.modes_api") public static final int SOURCE_UNKNOWN = 0; // 0x0 + field @FlaggedApi("android.app.modes_api") public static final int SOURCE_USER_ACTION = 1; // 0x1 field public static final int STATE_ERROR = 3; // 0x3 field public static final int STATE_FALSE = 0; // 0x0 field public static final int STATE_TRUE = 1; // 0x1 @@ -40549,6 +40556,7 @@ package android.service.notification { field public final android.net.Uri id; field public final String line1; field public final String line2; + field @FlaggedApi("android.app.modes_api") public final int source; field public final int state; field public final String summary; } diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index 4d33bfd1e94a..d76fa5be4940 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -16,8 +16,11 @@ package android.service.notification; +import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Flags; import android.content.Context; import android.net.Uri; import android.os.Parcel; @@ -57,7 +60,6 @@ public final class Condition implements Parcelable { * Indicates that Do Not Disturb should be turned on. */ public static final int STATE_TRUE = 1; - public static final int STATE_UNKNOWN = 2; public static final int STATE_ERROR = 3; @@ -90,6 +92,33 @@ public final class Condition implements Parcelable { public final int flags; public final int icon; + /** @hide */ + @IntDef(prefix = { "SOURCE_" }, value = { + SOURCE_UNKNOWN, + SOURCE_USER_ACTION, + SOURCE_SCHEDULE, + SOURCE_CONTEXT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Source {} + + /** The state is changing due to an unknown reason. */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int SOURCE_UNKNOWN = 0; + /** The state is changing due to an explicit user action. */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int SOURCE_USER_ACTION = 1; + /** The state is changing due to an automatic schedule (alarm, set time, etc). */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int SOURCE_SCHEDULE = 2; + /** The state is changing due to a change in context (such as detected driving or sleeping). */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final int SOURCE_CONTEXT = 3; + + /** The source of, or reason for, the state change represented by this Condition. **/ + @FlaggedApi(Flags.FLAG_MODES_API) + public final @Source int source; + /** * The maximum string length for any string contained in this condition. * @hide @@ -99,14 +128,48 @@ public final class Condition implements Parcelable { /** * An object representing the current state of a {@link android.app.AutomaticZenRule}. * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule - * @param summary a user visible description of the rule state. + * @param summary a user visible description of the rule state + * @param state whether the mode should be activated or deactivated */ + // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source. public Condition(Uri id, String summary, int state) { - this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS); + this(id, summary, "", "", -1, state, SOURCE_UNKNOWN, FLAG_RELEVANT_ALWAYS); + } + + /** + * An object representing the current state of a {@link android.app.AutomaticZenRule}. + * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule + * @param summary a user visible description of the rule state + * @param state whether the mode should be activated or deactivated + * @param source the source of, or reason for, the state change represented by this Condition + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public Condition(@Nullable Uri id, @Nullable String summary, @State int state, + @Source int source) { + this(id, summary, "", "", -1, state, source, FLAG_RELEVANT_ALWAYS); } + // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source. public Condition(Uri id, String summary, String line1, String line2, int icon, int state, int flags) { + this(id, summary, line1, line2, icon, state, SOURCE_UNKNOWN, flags); + } + + /** + * An object representing the current state of a {@link android.app.AutomaticZenRule}. + * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule + * @param summary a user visible description of the rule state + * @param line1 a user-visible description of when the rule will end + * @param line2 a continuation of the user-visible description of when the rule will end + * @param icon an icon representing this condition + * @param state whether the mode should be activated or deactivated + * @param source the source of, or reason for, the state change represented by this Condition + * @param flags flags on this condition + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public Condition(@Nullable Uri id, @Nullable String summary, @Nullable String line1, + @Nullable String line2, int icon, @State int state, @Source int source, + int flags) { if (id == null) throw new IllegalArgumentException("id is required"); if (summary == null) throw new IllegalArgumentException("summary is required"); if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state); @@ -116,6 +179,7 @@ public final class Condition implements Parcelable { this.line2 = getTrimmedString(line2); this.icon = icon; this.state = state; + this.source = source; this.flags = flags; } @@ -129,6 +193,7 @@ public final class Condition implements Parcelable { source.readString(), source.readInt(), source.readInt(), + Flags.modesApi() ? source.readInt() : SOURCE_UNKNOWN, source.readInt()); } @@ -144,20 +209,27 @@ public final class Condition implements Parcelable { dest.writeString(line2); dest.writeInt(icon); dest.writeInt(state); + if (Flags.modesApi()) { + dest.writeInt(this.source); + } dest.writeInt(this.flags); } @Override public String toString() { - return new StringBuilder(Condition.class.getSimpleName()).append('[') + StringBuilder sb = new StringBuilder(Condition.class.getSimpleName()).append('[') .append("state=").append(stateToString(state)) .append(",id=").append(id) .append(",summary=").append(summary) .append(",line1=").append(line1) .append(",line2=").append(line2) - .append(",icon=").append(icon) - .append(",flags=").append(flags) + .append(",icon=").append(icon); + if (Flags.modesApi()) { + sb.append(",source=").append(sourceToString(source)); + } + return sb.append(",flags=").append(flags) .append(']').toString(); + } /** @hide */ @@ -171,6 +243,7 @@ public final class Condition implements Parcelable { proto.write(ConditionProto.LINE_2, line2); proto.write(ConditionProto.ICON, icon); proto.write(ConditionProto.STATE, state); + // TODO: b/310644464 - Add source to dump. proto.write(ConditionProto.FLAGS, flags); proto.end(token); @@ -184,6 +257,16 @@ public final class Condition implements Parcelable { throw new IllegalArgumentException("state is invalid: " + state); } + /** Provides a human-readable string version of the Source enum. */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static @NonNull String sourceToString(@Source int source) { + if (source == SOURCE_UNKNOWN) return "SOURCE_UNKNOWN"; + if (source == SOURCE_USER_ACTION) return "SOURCE_USER_ACTION"; + if (source == SOURCE_SCHEDULE) return "SOURCE_SCHEDULE"; + if (source == SOURCE_CONTEXT) return "SOURCE_CONTEXT"; + throw new IllegalArgumentException("source is invalid: " + source); + } + public static String relevanceToString(int flags) { final boolean now = (flags & FLAG_RELEVANT_NOW) != 0; final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0; @@ -197,17 +280,24 @@ public final class Condition implements Parcelable { if (!(o instanceof Condition)) return false; if (o == this) return true; final Condition other = (Condition) o; - return Objects.equals(other.id, id) + boolean finalEquals = Objects.equals(other.id, id) && Objects.equals(other.summary, summary) && Objects.equals(other.line1, line1) && Objects.equals(other.line2, line2) && other.icon == icon && other.state == state && other.flags == flags; + if (Flags.modesApi()) { + return finalEquals && other.source == source; + } + return finalEquals; } @Override public int hashCode() { + if (Flags.modesApi()) { + return Objects.hash(id, summary, line1, line2, icon, state, source, flags); + } return Objects.hash(id, summary, line1, line2, icon, state, flags); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 305b751f0cef..fedad895961d 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -160,6 +160,7 @@ public class ZenModeConfig implements Parcelable { private static final String CONDITION_ATT_LINE2 = "line2"; private static final String CONDITION_ATT_ICON = "icon"; private static final String CONDITION_ATT_STATE = "state"; + private static final String CONDITION_ATT_SOURCE = "source"; private static final String CONDITION_ATT_FLAGS = "flags"; private static final String ZEN_POLICY_TAG = "zen_policy"; @@ -687,7 +688,12 @@ public class ZenModeConfig implements Parcelable { final int state = safeInt(parser, CONDITION_ATT_STATE, -1); final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1); try { - return new Condition(id, summary, line1, line2, icon, state, flags); + if (Flags.modesApi()) { + final int source = safeInt(parser, CONDITION_ATT_SOURCE, Condition.SOURCE_UNKNOWN); + return new Condition(id, summary, line1, line2, icon, state, source, flags); + } else { + return new Condition(id, summary, line1, line2, icon, state, flags); + } } catch (IllegalArgumentException e) { Slog.w(TAG, "Unable to read condition xml", e); return null; @@ -701,6 +707,9 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, CONDITION_ATT_LINE2, c.line2); out.attributeInt(null, CONDITION_ATT_ICON, c.icon); out.attributeInt(null, CONDITION_ATT_STATE, c.state); + if (Flags.modesApi()) { + out.attributeInt(null, CONDITION_ATT_SOURCE, c.source); + } out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags); } diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java index 42629ba41287..612562eb22dc 100644 --- a/core/tests/coretests/src/android/service/notification/ConditionTest.java +++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java @@ -16,17 +16,22 @@ package android.service.notification; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import android.app.Flags; import android.net.Uri; import android.os.Parcel; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.google.common.base.Strings; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,8 +42,11 @@ import java.lang.reflect.Field; public class ConditionTest { private static final String CLASS = "android.service.notification.Condition"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Test - public void testLongFields_inConstructors() { + public void testLongFields_inConstructors_classic() { String longString = Strings.repeat("A", 65536); Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); @@ -59,7 +67,7 @@ public class ConditionTest { } @Test - public void testLongFields_viaParcel() { + public void testLongFields_viaParcel_classic() { // Set fields via reflection to force them to be long, then parcel and unparcel to make sure // it gets truncated upon unparcelling. Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder", @@ -98,4 +106,92 @@ public class ConditionTest { assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length()); assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length()); } + + @Test + public void testLongFields_inConstructors() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + + // Confirm strings are truncated via short constructor + Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE, + Condition.SOURCE_CONTEXT); + + assertThat(cond1.id.toString()).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(cond1.summary).hasLength(Condition.MAX_STRING_LENGTH); + + // Confirm strings are truncated via long constructor + Condition cond2 = new Condition(longUri, longString, longString, longString, + -1, Condition.STATE_TRUE, Condition.SOURCE_CONTEXT, Condition.FLAG_RELEVANT_ALWAYS); + + assertThat(cond2.id.toString()).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(cond2.summary).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(cond2.line1).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(cond2.line2).hasLength(Condition.MAX_STRING_LENGTH); + } + + @Test + public void testLongFields_viaParcel() throws Exception { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + // Set fields via reflection to force them to be long, then parcel and unparcel to make sure + // it gets truncated upon unparcelling. + Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder", + Condition.STATE_TRUE, Condition.SOURCE_CONTEXT); + + String longString = Strings.repeat("A", 65536); + Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530)); + Field id = Class.forName(CLASS).getDeclaredField("id"); + id.setAccessible(true); + id.set(cond, longUri); + Field summary = Class.forName(CLASS).getDeclaredField("summary"); + summary.setAccessible(true); + summary.set(cond, longString); + Field line1 = Class.forName(CLASS).getDeclaredField("line1"); + line1.setAccessible(true); + line1.set(cond, longString); + Field line2 = Class.forName(CLASS).getDeclaredField("line2"); + line2.setAccessible(true); + line2.set(cond, longString); + + Parcel parcel = Parcel.obtain(); + cond.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Condition fromParcel = new Condition(parcel); + assertThat(fromParcel.id.toString()).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(fromParcel.summary).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(fromParcel.line1).hasLength(Condition.MAX_STRING_LENGTH); + assertThat(fromParcel.line2).hasLength(Condition.MAX_STRING_LENGTH); + } + + @Test + public void testEquals() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + + Condition cond1 = new Condition(Uri.parse("uri://placeholder"), "placeholder", + Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION); + Condition cond2 = new Condition(Uri.parse("uri://placeholder"), "placeholder", + "", "", -1, + Condition.STATE_TRUE, Condition.SOURCE_SCHEDULE, Condition.FLAG_RELEVANT_ALWAYS); + + assertThat(cond1).isNotEqualTo(cond2); + Condition cond3 = new Condition(Uri.parse("uri://placeholder"), "placeholder", + Condition.STATE_TRUE, Condition.SOURCE_SCHEDULE); + assertThat(cond3).isEqualTo(cond2); + } + + @Test + public void testParcelConstructor() { + mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); + + Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder", + Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION); + + Parcel parcel = Parcel.obtain(); + cond.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Condition fromParcel = new Condition(parcel); + assertThat(fromParcel).isEqualTo(cond); + } } |