1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.wm.shell.dagger; 18 19 import java.lang.annotation.Documented; 20 import java.lang.annotation.Inherited; 21 import java.lang.annotation.Retention; 22 import java.lang.annotation.RetentionPolicy; 23 24 import javax.inject.Qualifier; 25 26 /** 27 * This is a qualifier that Shell uses to workaround an issue with providing nullable optionals 28 * which are by default unbound. 29 * 30 * For example, ideally we would have this scenario: 31 * BaseModule: 32 * @BindsOptionalOf 33 * abstract Optional<Interface> optionalInterface(); 34 * 35 * SpecializedModule: 36 * @Provides 37 * static Interface providesInterface() { 38 * return new InterfaceImpl(); 39 * } 40 * 41 * However, if the interface is supposed to be provided dynamically, then Dagger is not able to bind 42 * the optional interface to a null instance, and @BindsOptionalOf does not support @Nullable 43 * instances of the interface provided by the specialized module. 44 * 45 * For example, this does not work: 46 * BaseModule: 47 * @BindsOptionalOf 48 * abstract Optional<Interface> optionalInterface(); 49 * 50 * SpecializedModule: 51 * @Provides 52 * static Interface providesInterface() { 53 * if (systemSupportsInterfaceFeature) { 54 * return new InterfaceImpl(); 55 * } else { 56 * return null; 57 * } 58 * } 59 * 60 * To workaround this, we can instead upstream the check (assuming it can be upstreamed into the 61 * base module), and then always provide a non-null instance in the specialized module. 62 * 63 * For example: 64 * BaseModule: 65 * @BindsOptionalOf 66 * @DynamicOverride 67 * abstract Interface dynamicInterface(); 68 * 69 * @Provides 70 * static Optional<Interface> providesOptionalInterface( 71 * @DynamicOverride Optional<Interface> interface) { 72 * if (systemSupportsInterfaceFeature) { 73 * return interface; 74 * } 75 * return Optional.empty(); 76 * } 77 * 78 * SpecializedModule: 79 * @Provides 80 * @DynamicOverride 81 * static Interface providesInterface() { 82 * return new InterfaceImpl(); 83 * } 84 * 85 * This is also useful in cases where there needs to be a default implementation in the base module 86 * which is also overridable in the specialized module. This isn't generally recommended, but 87 * due to the nature of Shell modules being referenced from a number of various projects, this 88 * can be useful for *required* components that 89 * 1) clearly identifies which are intended for overriding in the base module, and 90 * 2) allows us to declare a default implementation in the base module, without having to force 91 * every SysUI impl to explicitly provide it (if a large number of them share the default impl) 92 * 93 * For example, this uses the same setup as above, but the interface provided (if bound) is used 94 * otherwise the default is created: 95 * 96 * BaseModule: 97 * @BindsOptionalOf 98 * @DynamicOverride 99 * abstract Interface dynamicInterface(); 100 * 101 * @Provides 102 * static Optional<Interface> providesOptionalInterface( 103 * @DynamicOverride Optional<Interface> overrideInterfaceImpl) { 104 * if (overrideInterfaceImpl.isPresent()) { 105 * return overrideInterfaceImpl.get(); 106 * } 107 * return new DefaultImpl(); 108 * } 109 * 110 * SpecializedModule: 111 * @Provides 112 * @DynamicOverride 113 * static Interface providesInterface() { 114 * return new SuperSpecialImpl(); 115 * } 116 */ 117 @Documented 118 @Inherited 119 @Qualifier 120 @Retention(RetentionPolicy.RUNTIME) 121 public @interface DynamicOverride {}