Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f2a17ba authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 9261117 from 8fd3032f to tm-qpr2-release

Change-Id: I7c34dedc90f9eddf0c664f93d6c7ea41e3871908
parents c34b63f5 8fd3032f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -115,6 +115,7 @@ android_library {
        "androidx-constraintlayout_constraintlayout",
        "androidx-constraintlayout_constraintlayout",
        "androidx.exifinterface_exifinterface",
        "androidx.exifinterface_exifinterface",
        "com.google.android.material_material",
        "com.google.android.material_material",
        "kotlin-reflect",
        "kotlinx_coroutines_android",
        "kotlinx_coroutines_android",
        "kotlinx_coroutines",
        "kotlinx_coroutines",
        "iconloader_base",
        "iconloader_base",
+13 −13
Original line number Original line Diff line number Diff line
@@ -89,7 +89,7 @@ abstract class BooleanFlag constructor(
 *
 *
 * It can be changed or overridden in debug builds but not in release builds.
 * It can be changed or overridden in debug builds but not in release builds.
 */
 */
data class UnreleasedFlag @JvmOverloads constructor(
data class UnreleasedFlag constructor(
    override val id: Int,
    override val id: Int,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
    override val overridden: Boolean = false
    override val overridden: Boolean = false
@@ -100,7 +100,7 @@ data class UnreleasedFlag @JvmOverloads constructor(
 *
 *
 * It can be changed or overridden in any build, meaning it can be turned off if needed.
 * It can be changed or overridden in any build, meaning it can be turned off if needed.
 */
 */
data class ReleasedFlag @JvmOverloads constructor(
data class ReleasedFlag constructor(
    override val id: Int,
    override val id: Int,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
    override val overridden: Boolean = false
    override val overridden: Boolean = false
@@ -111,7 +111,7 @@ data class ReleasedFlag @JvmOverloads constructor(
 *
 *
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 */
 */
data class ResourceBooleanFlag @JvmOverloads constructor(
data class ResourceBooleanFlag constructor(
    override val id: Int,
    override val id: Int,
    @BoolRes override val resourceId: Int,
    @BoolRes override val resourceId: Int,
    override val teamfood: Boolean = false
    override val teamfood: Boolean = false
@@ -124,7 +124,7 @@ data class ResourceBooleanFlag @JvmOverloads constructor(
 *
 *
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 */
 */
data class DeviceConfigBooleanFlag @JvmOverloads constructor(
data class DeviceConfigBooleanFlag constructor(
    override val id: Int,
    override val id: Int,
    override val name: String,
    override val name: String,
    override val namespace: String,
    override val namespace: String,
@@ -139,7 +139,7 @@ data class DeviceConfigBooleanFlag @JvmOverloads constructor(
 *
 *
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 * Prefer [UnreleasedFlag] and [ReleasedFlag].
 */
 */
data class SysPropBooleanFlag @JvmOverloads constructor(
data class SysPropBooleanFlag constructor(
    override val id: Int,
    override val id: Int,
    override val name: String,
    override val name: String,
    override val default: Boolean = false
    override val default: Boolean = false
@@ -148,7 +148,7 @@ data class SysPropBooleanFlag @JvmOverloads constructor(
    override val teamfood: Boolean = false
    override val teamfood: Boolean = false
}
}


data class StringFlag @JvmOverloads constructor(
data class StringFlag constructor(
    override val id: Int,
    override val id: Int,
    override val default: String = "",
    override val default: String = "",
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
@@ -173,13 +173,13 @@ data class StringFlag @JvmOverloads constructor(
    }
    }
}
}


data class ResourceStringFlag @JvmOverloads constructor(
data class ResourceStringFlag constructor(
    override val id: Int,
    override val id: Int,
    @StringRes override val resourceId: Int,
    @StringRes override val resourceId: Int,
    override val teamfood: Boolean = false
    override val teamfood: Boolean = false
) : ResourceFlag<String>
) : ResourceFlag<String>


data class IntFlag @JvmOverloads constructor(
data class IntFlag constructor(
    override val id: Int,
    override val id: Int,
    override val default: Int = 0,
    override val default: Int = 0,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
@@ -205,13 +205,13 @@ data class IntFlag @JvmOverloads constructor(
    }
    }
}
}


data class ResourceIntFlag @JvmOverloads constructor(
data class ResourceIntFlag constructor(
    override val id: Int,
    override val id: Int,
    @IntegerRes override val resourceId: Int,
    @IntegerRes override val resourceId: Int,
    override val teamfood: Boolean = false
    override val teamfood: Boolean = false
) : ResourceFlag<Int>
) : ResourceFlag<Int>


data class LongFlag @JvmOverloads constructor(
data class LongFlag constructor(
    override val id: Int,
    override val id: Int,
    override val default: Long = 0,
    override val default: Long = 0,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
@@ -237,7 +237,7 @@ data class LongFlag @JvmOverloads constructor(
    }
    }
}
}


data class FloatFlag @JvmOverloads constructor(
data class FloatFlag constructor(
    override val id: Int,
    override val id: Int,
    override val default: Float = 0f,
    override val default: Float = 0f,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
@@ -263,13 +263,13 @@ data class FloatFlag @JvmOverloads constructor(
    }
    }
}
}


data class ResourceFloatFlag @JvmOverloads constructor(
data class ResourceFloatFlag constructor(
    override val id: Int,
    override val id: Int,
    override val resourceId: Int,
    override val resourceId: Int,
    override val teamfood: Boolean = false
    override val teamfood: Boolean = false
) : ResourceFlag<Int>
) : ResourceFlag<Int>


data class DoubleFlag @JvmOverloads constructor(
data class DoubleFlag constructor(
    override val id: Int,
    override val id: Int,
    override val default: Double = 0.0,
    override val default: Double = 0.0,
    override val teamfood: Boolean = false,
    override val teamfood: Boolean = false,
+13 −26
Original line number Original line Diff line number Diff line
@@ -23,7 +23,6 @@ import androidx.annotation.NonNull;
import com.android.systemui.statusbar.commandline.Command;
import com.android.systemui.statusbar.commandline.Command;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;


@@ -230,33 +229,22 @@ public class FlagCommand implements Command {
    }
    }


    private int flagNameToId(String flagName) {
    private int flagNameToId(String flagName) {
        List<Field> fields = Flags.getFlagFields();
        Map<String, Flag<?>> flagFields = Flags.getFlagFields();
        for (Field field : fields) {
        for (String fieldName : flagFields.keySet()) {
            if (flagName.equals(field.getName())) {
            if (flagName.equals(fieldName)) {
                return fieldToId(field);
                return flagFields.get(fieldName).getId();
            }
            }
        }
        }


        return 0;
        return 0;
    }
    }


    private int fieldToId(Field field) {
        try {
            Flag<?> flag = (Flag<?>) field.get(null);
            return flag.getId();
        } catch (IllegalAccessException e) {
            // no-op
        }

        return 0;
    }

    private void printKnownFlags(PrintWriter pw) {
    private void printKnownFlags(PrintWriter pw) {
        List<Field> fields = Flags.getFlagFields();
        Map<String, Flag<?>> fields = Flags.getFlagFields();


        int longestFieldName = 0;
        int longestFieldName = 0;
        for (Field field : fields) {
        for (String fieldName : fields.keySet()) {
            longestFieldName = Math.max(longestFieldName, field.getName().length());
            longestFieldName = Math.max(longestFieldName, fieldName.length());
        }
        }


        pw.println("Known Flags:");
        pw.println("Known Flags:");
@@ -268,16 +256,15 @@ public class FlagCommand implements Command {
        for (int i = 0; i < longestFieldName; i++) {
        for (int i = 0; i < longestFieldName; i++) {
            pw.print("=");
            pw.print("=");
        }
        }
        pw.println(" ==== =====");
        pw.println(" ==== ========");
        for (Field field : fields) {
        for (String fieldName : fields.keySet()) {
            int id = fieldToId(field);
            Flag<?> flag = fields.get(fieldName);
            Flag<?> flag = mAllFlags.get(id);
            int id = flag.getId();

            if (id == 0 || !mAllFlags.containsKey(id)) {
            if (id == 0 || !mAllFlags.containsKey(id)) {
                continue;
                continue;
            }
            }
            pw.print(field.getName());
            pw.print(fieldName);
            int fieldWidth = field.getName().length();
            int fieldWidth = fieldName.length();
            for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
            for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
                pw.print(" ");
                pw.print(" ");
            }
            }
+58 −28
Original line number Original line Diff line number Diff line
@@ -17,8 +17,12 @@ package com.android.systemui.flags


import android.provider.DeviceConfig
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
import com.android.internal.annotations.Keep
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.R
import java.lang.reflect.Field
import kotlin.reflect.KClass
import kotlin.reflect.full.declaredMembers
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.staticProperties


/**
/**
 * List of [Flag] objects for use in SystemUI.
 * List of [Flag] objects for use in SystemUI.
@@ -63,6 +67,7 @@ object Flags {
    @JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
    @JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
    val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
    val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
    val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
    val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)

    @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
    @JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
    // next id: 117
    // next id: 117


@@ -148,6 +153,7 @@ object Flags {
    // TODO(b/254512321): Tracking Bug
    // TODO(b/254512321): Tracking Bug
    @JvmField val COMBINED_QS_HEADERS = UnreleasedFlag(501, teamfood = true)
    @JvmField val COMBINED_QS_HEADERS = UnreleasedFlag(501, teamfood = true)
    val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)
    val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)

    @JvmField
    @JvmField
    val QS_USER_DETAIL_SHORTCUT =
    val QS_USER_DETAIL_SHORTCUT =
        ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
        ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
@@ -244,14 +250,14 @@ object Flags {
    @JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
    @JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)


    // 1100 - windowing
    // 1100 - windowing
    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_ENABLE_SHELL_TRANSITIONS =
    val WM_ENABLE_SHELL_TRANSITIONS =
        SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)
        SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)


    // TODO(b/254513207): Tracking Bug
    // TODO(b/254513207): Tracking Bug
    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_ENABLE_PARTIAL_SCREEN_SHARING =
    val WM_ENABLE_PARTIAL_SCREEN_SHARING =
        DeviceConfigBooleanFlag(
        DeviceConfigBooleanFlag(
            1102,
            1102,
@@ -262,30 +268,30 @@ object Flags {
        )
        )


    // TODO(b/254512674): Tracking Bug
    // TODO(b/254512674): Tracking Bug
    @JvmField
    @Keep
    @Keep
    @JvmField
    val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)
    val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)
    val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
    val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val ENABLE_FLING_TO_DISMISS_BUBBLE =
    val ENABLE_FLING_TO_DISMISS_BUBBLE =
        SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)
        SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val ENABLE_FLING_TO_DISMISS_PIP =
    val ENABLE_FLING_TO_DISMISS_PIP =
        SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)
        SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
    val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
        SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
        SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)


@@ -293,18 +299,18 @@ object Flags {
    @JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)
    @JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)


    // 1200 - predictive back
    // 1200 - predictive back
    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_ENABLE_PREDICTIVE_BACK =
    val WM_ENABLE_PREDICTIVE_BACK =
        SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)
        SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_ENABLE_PREDICTIVE_BACK_ANIM =
    val WM_ENABLE_PREDICTIVE_BACK_ANIM =
        SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)
        SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)


    @JvmField
    @Keep
    @Keep
    @JvmField
    val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
    val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
        SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)
        SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)


@@ -313,7 +319,7 @@ object Flags {


    // 1300 - screenshots
    // 1300 - screenshots
    // TODO(b/254512719): Tracking Bug
    // TODO(b/254512719): Tracking Bug
    @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, true)
    @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, teamfood = true)


    // TODO(b/254513155): Tracking Bug
    // TODO(b/254513155): Tracking Bug
    @JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
    @JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
@@ -327,7 +333,7 @@ object Flags {
    val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
    val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)


    // 1700 - clipboard
    // 1700 - clipboard
    @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, true)
    @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, teamfood = true)
    @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
    @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)


    // 1800 - shade container
    // 1800 - shade container
@@ -337,7 +343,7 @@ object Flags {
    @JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
    @JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")


    // 2000 - device controls
    // 2000 - device controls
    @Keep val USE_APP_PANELS = UnreleasedFlag(2000, true)
    @Keep @JvmField val USE_APP_PANELS = UnreleasedFlag(2000, teamfood = true)


    // 2100 - Falsing Manager
    // 2100 - Falsing Manager
    @JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100)
    @JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100)
@@ -348,23 +354,47 @@ object Flags {
    // |  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
    // |  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
    @JvmStatic
    @JvmStatic
    fun collectFlags(): Map<Int, Flag<*>> {
    fun collectFlags(): Map<Int, Flag<*>> {
        return flagFields
        return flagFields.mapKeys { field -> field.value.id }
            .map { field ->
                // field[null] returns the current value of the field.
                // See java.lang.Field#get
                val flag = field[null] as Flag<*>
                flag.id to flag
            }
            .toMap()
    }
    }


    // |  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
    // |  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  |
    @JvmStatic
    @JvmStatic
    val flagFields: List<Field>
    val flagFields: Map<String, Flag<*>>
        get() {
        get() = collectFlagsInClass(Flags)
            return Flags::class.java.fields.filter { f ->

                Flag::class.java.isAssignableFrom(f.type)
    @VisibleForTesting
    fun collectFlagsInClass(instance: Any): Map<String, Flag<*>> {
        val cls = instance::class
        val javaPropNames = cls.java.fields.map { it.name }
        val props = cls.declaredMembers
        val staticProps = cls.staticProperties
        val staticPropNames = staticProps.map { it.name }
        return props
            .mapNotNull { property ->
                if ((property.returnType.classifier as KClass<*>).isSubclassOf(Flag::class)) {
                    // Fields with @JvmStatic should be accessed via java mechanisms
                    if (javaPropNames.contains(property.name)) {
                        property.name to cls.java.getField(property.name)[null] as Flag<*>
                        // Fields with @Keep but not @JvmField. Don't do this.
                    } else if (staticPropNames.contains(property.name)) {
                        // The below code causes access violation exceptions. I don't know why.
                        // property.name to (property.call() as Flag<*>)
                        // property.name to (staticProps.find { it.name == property.name }!!
                        // .getter.call() as Flag<*>)
                        throw java.lang.RuntimeException(
                            "The {$property.name} flag needs @JvmField"
                        )
                        // Everything else. Skip the `get` prefixed fields that kotlin adds.
                    } else if (property.name.subSequence(0, 3) != "get") {
                        property.name to (property.call(instance) as Flag<*>)
                    } else {
                        null
                    }
                    }
                } else {
                    null
                }
            }
            .toMap()
    }
    }
    // |                                                           |
    // |                                                           |
    // \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
    // \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
+0 −119
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.flags;

import static com.google.common.truth.Truth.assertWithMessage;

import android.util.Pair;

import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;

import org.junit.Test;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SmallTest
public class FlagsTest extends SysuiTestCase {

    @Test
    public void testDuplicateFlagIdCheckWorks() {
        List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
        Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);

        assertWithMessage(generateAssertionMessage(duplicates))
                .that(duplicates.size()).isEqualTo(2);
    }

    @Test
    public void testNoDuplicateFlagIds() {
        List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
        Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);

        assertWithMessage(generateAssertionMessage(duplicates))
                .that(duplicates.size()).isEqualTo(0);
    }

    private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Duplicate flag keys found: {");
        for (int id : duplicates.keySet()) {
            stringBuilder
                    .append(" ")
                    .append(id)
                    .append(": [")
                    .append(String.join(", ", duplicates.get(id)))
                    .append("]");
        }
        stringBuilder.append(" }");

        return stringBuilder.toString();
    }

    private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
        List<Pair<String, Flag<?>>> flags = new ArrayList<>();

        Field[] fields = clz.getFields();

        for (Field field : fields) {
            Class<?> t = field.getType();
            if (Flag.class.isAssignableFrom(t)) {
                try {
                    flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
                } catch (IllegalAccessException e) {
                    // no-op
                }
            }
        }

        return flags;
    }

    private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
        Map<Integer, List<String>> grouping = new HashMap<>();

        for (Pair<String, Flag<?>> flag : flags) {
            grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
            grouping.get(flag.second.getId()).add(flag.first);
        }

        Map<Integer, List<String>> result = new HashMap<>();
        for (Integer id : grouping.keySet()) {
            if (grouping.get(id).size() > 1) {
                result.put(id, grouping.get(id));
            }
        }

        return result;
    }

    private static class DuplicateFlagContainer {
        public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
        public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
        public static final StringFlag C_FLAG = new StringFlag(0);

        public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);

        public static final DoubleFlag E_FLAG = new DoubleFlag(3);
        public static final DoubleFlag F_FLAG = new DoubleFlag(3);
    }
}
Loading