diff options
| author | 2023-11-17 16:06:49 +0000 | |
|---|---|---|
| committer | 2023-11-17 16:06:49 +0000 | |
| commit | 77d95dbc5f635bb3febb97601348ceaf25bf9fc7 (patch) | |
| tree | ee1ac02d6b64a89cf4abcbd179983e8e6fc2a2d4 | |
| parent | 173f2b4d70b802e2fb11ced71c0cbbfc7d7b372d (diff) | |
| parent | 18225951a4066f238bf1bcdc3ba9095814c0a9b6 (diff) | |
Merge "Save/load ZenDeviceEffects as part of ZenRule" into main
7 files changed, 207 insertions, 27 deletions
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java index 5b096c641f78..db0b7ffa0913 100644 --- a/core/java/android/service/notification/ZenDeviceEffects.java +++ b/core/java/android/service/notification/ZenDeviceEffects.java @@ -178,6 +178,16 @@ public final class ZenDeviceEffects implements Parcelable { return mMaximizeDoze; } + /** + * Whether any of the effects are set up. + * @hide + */ + public boolean hasEffects() { + return mGrayscale || mSuppressAmbientDisplay || mDimWallpaper || mNightMode + || mDisableAutoBrightness || mDisableTapToWake || mDisableTiltToWake + || mDisableTouch || mMinimizeRadioUsage || mMaximizeDoze; + } + /** {@link Parcelable.Creator} that instantiates {@link ZenDeviceEffects} objects. */ @NonNull public static final Creator<ZenDeviceEffects> CREATOR = new Creator<ZenDeviceEffects>() { diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index fedad895961d..f1d35b5b1185 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -25,6 +25,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; +import android.annotation.FlaggedApi; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AlarmManager; @@ -185,6 +186,18 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ICON = "rule_icon"; private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; + private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale"; + private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY = + "zdeSuppressAmbientDisplay"; + private static final String DEVICE_EFFECT_DIM_WALLPAPER = "zdeDimWallpaper"; + private static final String DEVICE_EFFECT_USE_NIGHT_MODE = "zdeUseNightMode"; + private static final String DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS = "zdeDisableAutoBrightness"; + private static final String DEVICE_EFFECT_DISABLE_TAP_TO_WAKE = "zdeDisableTapToWake"; + private static final String DEVICE_EFFECT_DISABLE_TILT_TO_WAKE = "zdeDisableTiltToWake"; + private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch"; + private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage"; + private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze"; + @UnsupportedAppUsage public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; public boolean allowMedia = DEFAULT_ALLOW_MEDIA; @@ -630,6 +643,7 @@ public class ZenModeConfig implements Parcelable { rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); rt.zenPolicy = readZenPolicyXml(parser); if (Flags.modesApi()) { + rt.zenDeviceEffects = readZenDeviceEffectsXml(parser); rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false); rt.iconResId = safeInt(parser, RULE_ATT_ICON, 0); rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC); @@ -667,6 +681,9 @@ public class ZenModeConfig implements Parcelable { if (rule.zenPolicy != null) { writeZenPolicyXml(rule.zenPolicy, out); } + if (Flags.modesApi() && rule.zenDeviceEffects != null) { + writeZenDeviceEffectsXml(rule.zenDeviceEffects, out); + } out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified); if (Flags.modesApi()) { out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation); @@ -859,6 +876,57 @@ public class ZenModeConfig implements Parcelable { } } + @Nullable + private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) { + ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale( + safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false)) + .setShouldSuppressAmbientDisplay( + safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false)) + .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false)) + .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false)) + .setShouldDisableAutoBrightness( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false)) + .setShouldDisableTapToWake( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false)) + .setShouldDisableTiltToWake( + safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false)) + .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false)) + .setShouldMinimizeRadioUsage( + safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false)) + .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false)) + .build(); + + return deviceEffects.hasEffects() ? deviceEffects : null; + } + + private static void writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects, + TypedXmlSerializer out) throws IOException { + writeBooleanIfTrue(out, DEVICE_EFFECT_DISPLAY_GRAYSCALE, + deviceEffects.shouldDisplayGrayscale()); + writeBooleanIfTrue(out, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, + deviceEffects.shouldSuppressAmbientDisplay()); + writeBooleanIfTrue(out, DEVICE_EFFECT_DIM_WALLPAPER, deviceEffects.shouldDimWallpaper()); + writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_MODE, deviceEffects.shouldUseNightMode()); + writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, + deviceEffects.shouldDisableAutoBrightness()); + writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, + deviceEffects.shouldDisableTapToWake()); + writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, + deviceEffects.shouldDisableTiltToWake()); + writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TOUCH, deviceEffects.shouldDisableTouch()); + writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, + deviceEffects.shouldMinimizeRadioUsage()); + writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze()); + } + + private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value) + throws IOException { + if (value) { + out.attributeBoolean(null, att, true); + } + } + public static boolean isValidHour(int val) { return val >= 0 && val < 24; } @@ -1755,6 +1823,8 @@ public class ZenModeConfig implements Parcelable { // package name, only used for manual rules when they have turned DND on. public String enabler; public ZenPolicy zenPolicy; + @FlaggedApi(Flags.FLAG_MODES_API) + @Nullable public ZenDeviceEffects zenDeviceEffects; public boolean modified; // rule has been modified from initial creation public String pkg; public int type = AutomaticZenRule.TYPE_UNKNOWN; @@ -1784,6 +1854,9 @@ public class ZenModeConfig implements Parcelable { enabler = source.readString(); } zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); + if (Flags.modesApi()) { + zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class); + } modified = source.readInt() == 1; pkg = source.readString(); if (Flags.modesApi()) { @@ -1828,6 +1901,9 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(0); } dest.writeParcelable(zenPolicy, 0); + if (Flags.modesApi()) { + dest.writeParcelable(zenDeviceEffects, 0); + } dest.writeInt(modified ? 1 : 0); dest.writeString(pkg); if (Flags.modesApi()) { @@ -1859,7 +1935,8 @@ public class ZenModeConfig implements Parcelable { .append(",condition=").append(condition); if (Flags.modesApi()) { - sb.append(",allowManualInvocation=").append(allowManualInvocation) + sb.append(",deviceEffects=").append(zenDeviceEffects) + .append(",allowManualInvocation=").append(allowManualInvocation) .append(",iconResId=").append(iconResId) .append(",triggerDescription=").append(triggerDescription) .append(",type=").append(type); @@ -1917,6 +1994,7 @@ public class ZenModeConfig implements Parcelable { if (Flags.modesApi()) { return finalEquals + && Objects.equals(other.zenDeviceEffects, zenDeviceEffects) && other.allowManualInvocation == allowManualInvocation && other.iconResId == iconResId && Objects.equals(other.triggerDescription, triggerDescription) @@ -1930,8 +2008,9 @@ public class ZenModeConfig implements Parcelable { public int hashCode() { if (Flags.modesApi()) { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component, configurationActivity, pkg, id, enabler, zenPolicy, modified, - allowManualInvocation, iconResId, triggerDescription, type); + component, configurationActivity, pkg, id, enabler, zenPolicy, + zenDeviceEffects, modified, allowManualInvocation, iconResId, + triggerDescription, type); } return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, component, configurationActivity, pkg, id, enabler, zenPolicy, modified); diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java index eb55e40f2c7b..f345d7cb88fd 100644 --- a/core/java/android/service/notification/ZenModeDiff.java +++ b/core/java/android/service/notification/ZenModeDiff.java @@ -452,11 +452,12 @@ public class ZenModeDiff { public static final String FIELD_CREATION_TIME = "creationTime"; public static final String FIELD_ENABLER = "enabler"; public static final String FIELD_ZEN_POLICY = "zenPolicy"; + public static final String FIELD_ZEN_DEVICE_EFFECTS = "zenDeviceEffects"; public static final String FIELD_MODIFIED = "modified"; public static final String FIELD_PKG = "pkg"; public static final String FIELD_ALLOW_MANUAL = "allowManualInvocation"; public static final String FIELD_ICON_RES = "iconResId"; - public static final String FIELD_TRIGGER = "triggerDescription"; + public static final String FIELD_TRIGGER_DESCRIPTION = "triggerDescription"; public static final String FIELD_TYPE = "type"; // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule @@ -534,19 +535,25 @@ public class ZenModeDiff { if (!Objects.equals(from.pkg, to.pkg)) { addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg)); } - if (!Objects.equals(from.triggerDescription, to.triggerDescription)) { - addField(FIELD_TRIGGER, - new FieldDiff<>(from.triggerDescription, to.triggerDescription)); - } - if (from.type != to.type) { - addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type)); - } - if (from.allowManualInvocation != to.allowManualInvocation) { - addField(FIELD_ALLOW_MANUAL, - new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation)); - } - if (!Objects.equals(from.iconResId, to.iconResId)) { - addField(FIELD_ICON_RES, new FieldDiff(from.iconResId, to.iconResId)); + if (android.app.Flags.modesApi()) { + if (!Objects.equals(from.zenDeviceEffects, to.zenDeviceEffects)) { + addField(FIELD_ZEN_DEVICE_EFFECTS, + new FieldDiff<>(from.zenDeviceEffects, to.zenDeviceEffects)); + } + if (!Objects.equals(from.triggerDescription, to.triggerDescription)) { + addField(FIELD_TRIGGER_DESCRIPTION, + new FieldDiff<>(from.triggerDescription, to.triggerDescription)); + } + if (from.type != to.type) { + addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type)); + } + if (from.allowManualInvocation != to.allowManualInvocation) { + addField(FIELD_ALLOW_MANUAL, + new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation)); + } + if (!Objects.equals(from.iconResId, to.iconResId)) { + addField(FIELD_ICON_RES, new FieldDiff<>(from.iconResId, to.iconResId)); + } } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index a1704c6619ad..cb05084ea3f0 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -860,6 +860,9 @@ public class ZenModeHelper { rule.enabled = automaticZenRule.isEnabled(); rule.modified = automaticZenRule.isModified(); rule.zenPolicy = automaticZenRule.getZenPolicy(); + if (Flags.modesApi()) { + rule.zenDeviceEffects = automaticZenRule.getDeviceEffects(); + } rule.zenMode = NotificationManager.zenModeFromInterruptionFilter( automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF); rule.configurationActivity = automaticZenRule.getConfigurationActivity(); @@ -888,6 +891,7 @@ public class ZenModeHelper { .setIconResId(rule.iconResId) .setType(rule.type) .setZenPolicy(rule.zenPolicy) + .setDeviceEffects(rule.zenDeviceEffects) .setEnabled(rule.enabled) .setInterruptionFilter( NotificationManager.zenModeToInterruptionFilter(rule.zenMode)) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java index 8dcf89bb8438..999e33c24322 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java @@ -102,4 +102,18 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase { assertThat(copy.shouldSuppressAmbientDisplay()).isTrue(); assertThat(copy.shouldDisplayGrayscale()).isFalse(); } + + @Test + public void hasEffects_none_returnsFalse() { + ZenDeviceEffects effects = new ZenDeviceEffects.Builder().build(); + assertThat(effects.hasEffects()).isFalse(); + } + + @Test + public void hasEffects_some_returnsTrue() { + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + assertThat(effects.hasEffects()).isTrue(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 261b5d33b635..d4661075a552 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -33,6 +33,7 @@ import android.os.Parcel; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.Condition; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenPolicy; @@ -327,7 +328,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.conditionId = CONDITION_ID; rule.condition = CONDITION; rule.enabled = ENABLED; - rule.creationTime = 123; rule.id = "id"; rule.zenMode = INTERRUPTION_FILTER; rule.modified = true; @@ -335,6 +335,18 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.snoozing = true; rule.pkg = OWNER.getPackageName(); rule.zenPolicy = POLICY; + rule.zenDeviceEffects = new ZenDeviceEffects.Builder() + .setShouldDisplayGrayscale(false) + .setShouldSuppressAmbientDisplay(true) + .setShouldDimWallpaper(false) + .setShouldUseNightMode(true) + .setShouldDisableAutoBrightness(false) + .setShouldDisableTapToWake(true) + .setShouldDisableTiltToWake(false) + .setShouldDisableTouch(true) + .setShouldMinimizeRadioUsage(false) + .setShouldMaximizeDoze(true) + .build(); rule.creationTime = CREATION_TIME; rule.allowManualInvocation = ALLOW_MANUAL; @@ -362,6 +374,8 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.name, fromXml.name); assertEquals(rule.zenMode, fromXml.zenMode); assertEquals(rule.creationTime, fromXml.creationTime); + assertEquals(rule.zenPolicy, fromXml.zenPolicy); + assertEquals(rule.zenDeviceEffects, fromXml.zenDeviceEffects); assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation); assertEquals(rule.type, fromXml.type); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index fd3d5e9bf863..ed7e8ae5c6e5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -28,8 +30,10 @@ import android.content.ComponentName; import android.net.Uri; import android.provider.Settings; import android.service.notification.Condition; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeDiff; +import android.service.notification.ZenModeDiff.RuleDiff; import android.service.notification.ZenPolicy; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -42,10 +46,14 @@ import com.android.server.UiServiceTestCase; import org.junit.Test; import org.junit.runner.RunWith; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Set; @SmallTest @@ -56,6 +64,15 @@ public class ZenModeDiffTest extends UiServiceTestCase { public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS = Set.of("version", "manualRule", "automaticRules"); + // Differences for flagged fields are only generated if the flag is enabled. + // TODO: b/310620812 - Remove this exempt list when flag is inlined. + private static final Set<String> ZEN_RULE_EXEMPT_FIELDS = + android.app.Flags.modesApi() + ? Set.of() + : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION, + RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL, + RuleDiff.FIELD_ZEN_DEVICE_EFFECTS); + @Test public void testRuleDiff_addRemoveSame() { // Test add, remove, and both sides same @@ -86,7 +103,7 @@ public class ZenModeDiffTest extends UiServiceTestCase { ArrayMap<String, Object> expectedFrom = new ArrayMap<>(); ArrayMap<String, Object> expectedTo = new ArrayMap<>(); List<Field> fieldsForDiff = getFieldsForDiffCheck( - ZenModeConfig.ZenRule.class, Set.of()); // actually no exempt fields for ZenRule + ZenModeConfig.ZenRule.class, ZEN_RULE_EXEMPT_FIELDS); generateFieldDiffs(r1, r2, fieldsForDiff, expectedFrom, expectedTo); ZenModeDiff.RuleDiff d = new ZenModeDiff.RuleDiff(r1, r2); @@ -230,16 +247,21 @@ public class ZenModeDiffTest extends UiServiceTestCase { rule.name = "name"; rule.snoozing = true; rule.pkg = "a"; - rule.allowManualInvocation = true; - rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME; - rule.iconResId = 123; - rule.triggerDescription = "At night"; + if (android.app.Flags.modesApi()) { + rule.allowManualInvocation = true; + rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME; + rule.iconResId = 123; + rule.triggerDescription = "At night"; + rule.zenDeviceEffects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + } return rule; } // Get the fields on which we would want to check a diff. The requirements are: not final or/ // static (as these should/can never change), and not in a specific list that's exempted. - private List<Field> getFieldsForDiffCheck(Class c, Set<String> exemptNames) + private List<Field> getFieldsForDiffCheck(Class<?> c, Set<String> exemptNames) throws SecurityException { Field[] fields = c.getDeclaredFields(); ArrayList<Field> out = new ArrayList<>(); @@ -272,7 +294,7 @@ public class ZenModeDiffTest extends UiServiceTestCase { f.setAccessible(true); // Just double-check also that the fields actually are for the class declared assertEquals(f.getDeclaringClass(), a.getClass()); - Class t = f.getType(); + Class<?> t = f.getType(); // handle the full set of primitive types first if (boolean.class.equals(t)) { f.setBoolean(a, true); @@ -305,8 +327,8 @@ public class ZenModeDiffTest extends UiServiceTestCase { f.set(a, null); expectedA.put(f.getName(), null); try { - f.set(b, t.getDeclaredConstructor().newInstance()); - expectedB.put(f.getName(), t.getDeclaredConstructor().newInstance()); + f.set(b, newInstanceOf(t)); + expectedB.put(f.getName(), newInstanceOf(t)); } catch (Exception e) { // No default constructor, or blithely attempting to construct something doesn't // work for some reason. If the default value isn't null, then keep it. @@ -321,4 +343,34 @@ public class ZenModeDiffTest extends UiServiceTestCase { } } } + + private static Object newInstanceOf(Class<?> clazz) throws ReflectiveOperationException { + try { + Constructor<?> defaultConstructor = clazz.getDeclaredConstructor(); + return defaultConstructor.newInstance(); + } catch (Exception e) { + // No default constructor, continue below. + } + + // Look for a suitable builder. + Optional<Class<?>> clazzBuilder = + Arrays.stream(clazz.getDeclaredClasses()) + .filter(maybeBuilder -> maybeBuilder.getSimpleName().equals("Builder")) + .filter(maybeBuilder -> + Arrays.stream(maybeBuilder.getMethods()).anyMatch( + m -> m.getName().equals("build") + && m.getParameterCount() == 0 + && m.getReturnType().equals(clazz))) + .findFirst(); + if (clazzBuilder.isPresent()) { + Object builder = newInstanceOf(clazzBuilder.get()); + Method buildMethod = builder.getClass().getMethod("build"); + Object built = buildMethod.invoke(builder); + assertThat(built).isInstanceOf(clazz); + return built; + } + + throw new ReflectiveOperationException( + "Sorry! Couldn't figure out how to create an instance of " + clazz.getName()); + } } |