blob: b867ec89d70893c053418cb592595b971b77fd3e [file] [log] [blame]
/*
* Copyright (C) 2021 The Dagger Authors.
*
* 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 dagger.hilt.android.flags;
import android.content.Context;
import dagger.Module;
import dagger.hilt.EntryPoint;
import dagger.hilt.InstallIn;
import dagger.hilt.android.EntryPointAccessors;
import dagger.hilt.components.SingletonComponent;
import dagger.hilt.internal.Preconditions;
import dagger.multibindings.Multibinds;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.Set;
import javax.inject.Qualifier;
/**
* Runtime flag for the Fragment.getContext() fix. See https://github.com/google/dagger/pull/2620
* for this change. Controls if fragment code should use the fixed getContext() behavior where it
* correctly returns null after a fragment is removed. This fixed behavior matches the behavior of a
* regular, non-Hilt fragment and can help catch issues where a removed or leaked fragment is
* incorrectly used.
*
* <p>This flag is paired with the compiler option flag
* dagger.hilt.android.useFragmentGetContextFix. When that flag is false, this runtime flag has no
* effect on behavior (e.g. the compiler flag being off takes precedence). When the compiler flag is
* on, then the runtime flag may be used to disable the behavior at runtime.
*
* <p>In order to set the flag, bind a boolean value qualified with
* {@link DisableFragmentGetContextFix} into a set in the {@code SingletonComponent}. A set is used
* instead of an optional binding to avoid a dependency on Guava. Only one value may be bound into
* the set within a given app. Example for binding the value:
*
* <pre><code>
* {@literal @}Module
* {@literal @}InstallIn(SingletonComponent.class)
* public final class DisableFragmentGetContextFixModule {
* {@literal @}Provides
* {@literal @}IntoSet
* {@literal @}FragmentGetContextFix.DisableFragmentGetContextFix
* static Boolean provideDisableFragmentGetContextFix() {
* return // true or false depending on some rollout logic for your app
* }
* }
* </code></pre>
*/
public final class FragmentGetContextFix {
/** Qualifier annotation to bind disable the Fragment.getContext() fix at runtime. */
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Qualifier
public @interface DisableFragmentGetContextFix {}
public static boolean isFragmentGetContextFixDisabled(Context context) {
// Use a set here instead of an optional to avoid the Guava dependency
Set<Boolean> flagSet = EntryPointAccessors.fromApplication(
context, FragmentGetContextFixEntryPoint.class).getDisableFragmentGetContextFix();
// TODO(b/199927963): Consider adding a plugin to check this at compile time
Preconditions.checkState(flagSet.size() <= 1,
"Cannot bind the flag @DisableFragmentGetContextFix more than once.");
if (flagSet.isEmpty()) {
return false;
} else {
return flagSet.iterator().next();
}
}
/** Entry point for getting the flag. */
@EntryPoint
@InstallIn(SingletonComponent.class)
public interface FragmentGetContextFixEntryPoint {
@DisableFragmentGetContextFix Set<Boolean> getDisableFragmentGetContextFix();
}
/** Declare the empty flag set. */
@Module
@InstallIn(SingletonComponent.class)
abstract static class FragmentGetContextFixModule {
@Multibinds
@DisableFragmentGetContextFix
abstract Set<Boolean> disableFragmentGetContextFix();
}
private FragmentGetContextFix() {
}
}