{"id":19878701,"url":"https://github.com/ironsource/aircon","last_synced_at":"2025-05-02T13:30:47.071Z","repository":{"id":37865040,"uuid":"187631869","full_name":"ironSource/aircon","owner":"ironSource","description":"Remote config management Android library powered by annotation processing and code generation.","archived":false,"fork":false,"pushed_at":"2023-03-23T18:56:24.000Z","size":667,"stargazers_count":62,"open_issues_count":2,"forks_count":7,"subscribers_count":20,"default_branch":"master","last_synced_at":"2024-06-20T05:50:08.738Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ironSource.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-20T12:00:18.000Z","updated_at":"2023-09-27T13:47:30.000Z","dependencies_parsed_at":"2023-01-21T12:30:37.540Z","dependency_job_id":null,"html_url":"https://github.com/ironSource/aircon","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironSource%2Faircon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironSource%2Faircon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironSource%2Faircon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ironSource%2Faircon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ironSource","download_url":"https://codeload.github.com/ironSource/aircon/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224315370,"owners_count":17290992,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T17:06:25.276Z","updated_at":"2024-11-12T17:06:26.516Z","avatar_url":"https://github.com/ironSource.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Logo](images/logo.png)\n[ ![Download](https://api.bintray.com/packages/ironsource-aura/AirCon/aircon/images/download.svg) ](https://bintray.com/ironsource-aura/AirCon/aircon/_latestVersion)\n\nAirCon\n============\n\nControl over the air\n---\nRemote config management Android library powered by annotation processing and code generation.\nhttps://medium.com/@hanan_rofe_haim/remote-config-in-android-one-release-to-rule-them-all-5ffa7750dec9\n\n * Manage your app's remote configs using simple annotations, use generated providers classes to obtain config values.\n * Supports adding any config source (FireBase support included in library).\n * Supports custom validation and adaptation of config values.\n * Inject remotely configured colors/Strings directly to XML layouts.\n \nUsage\n---\n```java\n@Source(FireBaseConfigSource.class) // Can use any custom source instead of Firebase\n@FeatureRemoteConfig\ninterface CoolFeature {\n    \t\n    @BooleanConfig(defaultValue = false)\n    String ENABLED = \"enabled\";\n    \t\n    @Mutable\n    @IntConfig(defaultValue = 1, maxValue = 3)\n    String MAX_TIMES_TO_SHOW = \"maxTimeToShow\";\n    \t\n    @StringConfig(defaultValue = \"Hello world\")\n    String MESSAGE = \"msg\";\n}\n```\n\n```java\nfinal boolean enabled = CoolFeatureConfigProvider.isEnabled();\nfinal int maxTimesToShow = CoolFeatureConfigProvider.getMaxTimesToShow();\nfinal String message = CoolFeatureConfigProvider.getMessage();\n```\n\nInitializing the SDK\n---\nThe AirCon SDK should be initialized in the Application.onCreate() method using a `AirConConfiguration` object. \n```java\npublic class App extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        AirCon.get().init(new AirConConfiguration.Builder(this).setLoggingEnabled(BuildConfig.DEBUG)\n\t\t                                               .setJsonConverter(new GsonConverter())\n\t\t                                               .addConfigSource(new FireBaseConfigSource(this, FirebaseRemoteConfig.getInstance())\n\t\t                                               .build());\n    }\n}\n```\n\nSupported config types\n--------\n#### Primitives\n* `@BooleanConfig`\n* `@IntConfig`\n* `@LongConfig`\n* `@FloatConfig`\n* `@StringConfig`\n\n#### Collection\n* `@StringSetConfig`\n\n#### Json\n* `@JsonConfig`\n\n#### Enums\n* `@StringEnumConfig`\n* `@IntEnumConfig`\n\n#### Special\n* `@ColorConfig`\n* `@TimeConfig`\n* `@TextConfig`\n* `@UrlConfig`\n* `@HtmlConfig`\n\nMutable configs\n--------\nAll configs are read-only by default.\nIf the custom source supports overriding local values (e.g the built-in Firebase config source) use the `@Mutable` annotation\non a config field to generate a setter for that config.\n\nRemote config sources\n--------\nAirCon supports any source of remote config.\nFor the common case of using FireBase as a remote config source, the library is packaged with a `FireBaseConfigSource`.\nConfig sources can either be added in the SDK init phase:\n```java\nAirCon.get().init(new AirConConfiguration.Builder(this).addConfigSource(new FireBaseConfigSource(this, FirebaseRemoteConfig.getInstance())\n```\nOr at a later stage:\n```java\nAirCon.get().getConfigSourceRepository().addSource(new FireBaseConfigSource(this, FirebaseRemoteConfig.getInstance());\n```\n\nMultiple remote config sources for the same app are supported.\n\nEach config is a assigned a source using the `@Source` annotation.\nEither define a source for the entire feature:\n```java\t\n@Source(FireBaseConfigSource.class)\n@FeatureRemoteConfig\ninterface CoolFeature {\n    \t\n    @BooleanConfig(defaultValue = false)\n    String ENABLED = \"enabled\";\n```\n\nOr define a specific source for each config:\n```java\n@Source(FireBaseConfigSource.class)\n@StringConfig(defaultValue = \"Hello world\")\nString MESSAGE = \"msg\";\n```\n\nIf a source is defined on both the feature interface and the config field, the config field source will be used.\n\nDefault value\n--------\nA default value for a config can be defined in several ways (only one way can be used for the same config):\n\n* Define the `defaultValue` attribute of the config annotation:\n   ```java\n    @BooleanConfig(defaultValue = false)\n    String ENABLED = \"enabled\";\n\t```\n* Define a resource as the default value:\n   ```java\n\t@DefaultRes(R.string.title)\n    @StringConfig\n    String TITLE = \"title\";\n\t```\t\n* Define another config value as a default value:\n  ```java\n    @BooleanConfig(defaultValue = false)\n    String ENABLED = \"enabled\";\n\t\n\t@DefaultConfig(ENABLED) // If \"flag\" is not configured, the provider will fallback to the configured value of \"enabled\".\n    @BooleanConfig\n    String ANOTHER_FLAG = \"flag\";\n\t```\n* Define a custom default value provider method:\n   ```java\n   @BooleanConfig\n   String ENABLED = \"enabled\";\n\t\n   @ConfigDefaultValueProvider(ENABLED)\n   public static boolean getEnabledDefault() {\n\t\treturn Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.M;\n\t}\n    ```\n    \nCustom value validation\n--------\nSome config types provide inherit validation.\nFor example, the `@UrlConfig` verifies that the configured URL is a valid URL, otherwise the default value is returned. \nIf custom validation is needed for a config the then a validation predicate method can be defined:\n```java\n@ConfigValidator(TITLE)\npublic static boolean isTitleValid(String title) {\n   return title.length \u003c= 20; // If a title longer than 20 chars is configured, fallback to the default value.\n}\n```\n\nCustom adapters\n--------\nSome config types provide implementation for common adaptation needs.\nFor example, the `@JsonConfig` converts a remotely configured json string to an object.\nIn some cases extra processing is needed on the returned config value, which can be achieved be defining an adapter method:\n```java\n@ConfigAdapter(ENABLED)\npublic static boolean processEnabled(boolean enabled) {\n   return enabled \u0026\u0026 BuildConfig.DEBUG; // Prevent feature from being enabled in production builds.\n}\n```\n\nAn adapter method can return a different type than the original config type:\n```java\n@ConfigAdapter(TITLE)\npublic static Label processTitle(String title) {\n   return new Label(title);\n}\n```\n\nEnum configs\n--------\nA common use case is converting a remotely configured int/String to an Enum:\n```java\npublic enum TextLocation {\n   @RemoteIntValue(0) BOTTOM,\n   @RemoteIntValue(1) CENTER,\n   @RemoteIntValue(2) TOP\n}\n```\n```java\n@IntEnumConfig(defaultValue = 2, enumClass = TextLocation.class)\nString TEXT_LOCATION = \"textLocation\";\n```\n\nUsage:\n```java\nTextLocation textLocation = CoolFeatureConfigProvider.getTextLocation();\n```\n\n`@StringEnumConfig` is also supported for converting strings to enum consts.\n\nConfig groups\n--------\nMultiple configs can be groups together.\nA POJO class containing all the config values will be generated and can be passed around to methods.\n\nGroups can be used in 2 ways:\n1. For every feature annotated with `@FeatureRemoteConfig` a group containing all configs is generated.\nUsage:\n```java\nfinal CoolFeatureConfig config = CoolFeatureConfigProvider.getAll();\n```\n\n2. Creating a custom group and defining the contained configs.\nThe value of the field defines the group name.\n```java\n@ConfigGroup({MAX_TIMES_TO_SHOW, MESSAGE}) \nString MY_GROUP = \"myGroup\";\n```\nUsage:\n```java\nMyGroupConfig myGroupConfig = CoolFeatureConfigProvider.getMyGroup();\n```\n\nMock values\n--------\nMocking remotely configured values is useful for testing purposes.\nTo mock a config value a mock method should be defined:\n```java\n@ConfigMock(RemoteConfigs.CoolFeature.ENABLED)\npublic static boolean mockEnabled() {\n   return true;\n}\n```\n\nA good practice for preventing mocks in release builds is defining all the config mock methods in the same class and ignore it in git.\nIn addition, a custom lint check will throw an error if a config mock is defined in a release build.\n\nCustom config types\n--------\nAirCon supports defining custom config type annotations in cases where the built-in types are not sufficient\nor when common validation and processing is used across multiple config keys.\n\nTo define a custom config type:\n1. Define a config type resolver\n```java\npublic class LabelConfigResolver implements ConfigTypeResolver\u003cLabelConfig, String, Label\u003e {\n\n   @Override\n   public Class\u003cLabelConfig\u003e getAnnotationClass() {\n      return LabelConfig.class;\n   }\n\n   @Override\n   public boolean isValid(final LabelConfig annotation, final String value) {\n      final String[] invalidValues = annotation.invalidValues();\n      return !Arrays.asList(invalidValues).contains(value);\n   }\n\n   @Override\n   public Label process(final LabelConfig annotation, final String value) {\n      return Label.from(value);\n   }\n}\n```\nThe resolver defines:\n- The custom config annotation (defined in step 2) to which it defines the processing logic.\n- How a configured value is processed for the custom type.\n- The primitive type of the config (i.e the type that will be used remotely).\n- The processed type of the config, which can be whatever you like.\n\n2. Define a config type annotation and annotate it with `@ConfigType` and bind it to the resolver you've created via \n   the parameter to the annotation\n```java\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\n@ConfigType(LabelConfigResolver.class)\npublic @interface LabelConfig {\n\t\n   String[] invalidValues() default {};\n\t\n   String defaultValue() default \"\";\n}\n```\nThe annotation can contain any number of attributes but must contain a `defaultValue` attribute.\n\nNote that the annotation must be retained in runtime and be targeted only for fields.\n\n3. Register the custom config type when initializing the SDK\n```java\nnew AirConConfiguration.Builder(this).registerConfigType(new LabelConfigResolver())\n```\n\n4. Use the custom config\n```java\n@DefaultRes(R.string.app_name)\n@LabelConfig(defaultValue = \"\", invalidValues = {\"invalid1\", \"invalid2\"})\nString SOME_CUSTOM_LABEL = \"someCustomLabel\";\n```\n\nThe new config type acts exactly the same as the build-in config annotations and therefore\ncan be used in conjunction with all other AirCon features such as mocks, custom adapters, custom validators and so on.\n\nXML injection\n--------\nAircon is also equipped with XML injection capabilities.\nInjection for attributes of type String, color and color state list are supported.\nTo use XML injection:\n1. Enable XML injection by providing the app R.attr class and a config source:\n```java\nAirCon.get().init(new AirConConfiguration.Builder(context).enableXmlInjection(R.attr.class, fireBaseConfigSource);\n```\nOr provide a custom attribute resolver:\n```java\nAirCon.get().init(new AirConConfiguration.Builder(context).enableXmlInjection(R.attr.class, new AttributeResolver() {\n\t\t\t@Override\n\t\t\tpublic ColorStateList getColorStateList(final String attrName) {\n\t\t\t\t// return the remotely configured color state list for the `attrName` key\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Integer getColor(final String attrName) {\n\t\t\t\t// return the remotely configured color for the `attrName` key\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getString(final String attrName) {\n\t\t\t\t// return the remotely configured string for the `attrName` key\n\t\t\t}\n\t\t})\n```\n\n2. Define attributes corresponding to config keys:\n```xml\n\u003cresources\u003e\n    \u003cattr name=\"aTitle\" /\u003e\n    \u003cattr name=\"aTitleTextColor\" /\u003e\n\u003c/resources\u003e\n```\n\n3. Define default values in the app theme:\n```xml\n\u003cstyle name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\"\u003e\n\t\u003citem name=\"aTitle\"\u003eHey there!\u003c/item\u003e\n        \u003citem name=\"aTitleTextColor\"\u003e@color/white\u003c/item\u003e\n\u003c/style\u003e\n```\n\n4. Use attributes in XML:\n```xml\n\u003cTextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"?attr/aTitle\"\n            android:textColor=\"?attr/aTitleTextColor\" /\u003e\n```\n\n5. Extend either `AirConAppCompatActivity` or `AirConFragmentActivity`.\n6. Behold the magic.\n\n\nLint\n--------\nAirCon library is bundled with various lint checks for verifying correct usage of the library.\nNo extra integration is needed from the app side.\n\n\nDownload\n--------\n```groovy\ndependencies {\n    implementation 'com.github.ironSource.aircon:aircon:1.10.0'\n    annotationProcessor 'com.github.ironSource.aircon:aircon-compiler:1.10.0'\n}\n```\nFor using Firebase config source:\n```groovy\ndependencies {\n    implementation 'com.ironsource.aura.aircon:firebase-source:1.4.6'\n}\n```\nFor using `Gson` to parse `@JsonConfig`:\n```groovy\ndependencies {\n    implementation 'com.github.ironSource.aircon:converter-gson:1.10.0'\n}\n```\n\nIf you are using Kotlin, replace `annotationProcessor` with `kapt`.\n\nLicense\n-------\n\nCopyright (c) 2019 Hanan Rofe Haim\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fironsource%2Faircon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fironsource%2Faircon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fironsource%2Faircon/lists"}