/* * Copyright (C) 2020 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.internal.managers; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.activity.ComponentActivity; import dagger.Binds; import dagger.Module; import dagger.hilt.EntryPoint; import dagger.hilt.EntryPoints; import dagger.hilt.InstallIn; import dagger.hilt.android.ActivityRetainedLifecycle; import dagger.hilt.android.components.ActivityRetainedComponent; import dagger.hilt.android.internal.ThreadUtil; import dagger.hilt.android.internal.builders.ActivityRetainedComponentBuilder; import dagger.hilt.android.scopes.ActivityRetainedScoped; import dagger.hilt.components.SingletonComponent; import dagger.hilt.internal.GeneratedComponentManager; import java.util.HashSet; import java.util.Set; import javax.inject.Inject; /** A manager for the creation of components that survives activity configuration changes. */ final class ActivityRetainedComponentManager implements GeneratedComponentManager { /** Entry point for {@link ActivityRetainedComponentBuilder}. */ @EntryPoint @InstallIn(SingletonComponent.class) public interface ActivityRetainedComponentBuilderEntryPoint { ActivityRetainedComponentBuilder retainedComponentBuilder(); } /** Entry point for {@link Lifecycle}. */ @EntryPoint @InstallIn(ActivityRetainedComponent.class) public interface ActivityRetainedLifecycleEntryPoint { ActivityRetainedLifecycle getActivityRetainedLifecycle(); } static final class ActivityRetainedComponentViewModel extends ViewModel { private final ActivityRetainedComponent component; ActivityRetainedComponentViewModel(ActivityRetainedComponent component) { this.component = component; } ActivityRetainedComponent getComponent() { return component; } @Override protected void onCleared() { super.onCleared(); ActivityRetainedLifecycle lifecycle = EntryPoints.get(component, ActivityRetainedLifecycleEntryPoint.class) .getActivityRetainedLifecycle(); ((ActivityRetainedComponentManager.Lifecycle) lifecycle).dispatchOnCleared(); } } private final ViewModelProvider viewModelProvider; @Nullable private volatile ActivityRetainedComponent component; private final Object componentLock = new Object(); ActivityRetainedComponentManager(ComponentActivity activity) { this.viewModelProvider = new ViewModelProvider( activity, new ViewModelProvider.Factory() { @NonNull @Override @SuppressWarnings("unchecked") public T create(@NonNull Class aClass) { ActivityRetainedComponent component = EntryPoints.get( activity.getApplication(), ActivityRetainedComponentBuilderEntryPoint.class) .retainedComponentBuilder() .build(); return (T) new ActivityRetainedComponentViewModel(component); } }); } @Override public ActivityRetainedComponent generatedComponent() { if (component == null) { synchronized (componentLock) { if (component == null) { component = createComponent(); } } } return component; } private ActivityRetainedComponent createComponent() { return viewModelProvider.get(ActivityRetainedComponentViewModel.class).getComponent(); } /** The default implementation of {@link ActivityRetainedLifecycle}. */ @ActivityRetainedScoped static final class Lifecycle implements ActivityRetainedLifecycle { private final Set listeners = new HashSet<>(); private boolean onClearedDispatched = false; @Inject Lifecycle() {} @Override public void addOnClearedListener(@NonNull OnClearedListener listener) { ThreadUtil.ensureMainThread(); throwIfOnClearedDispatched(); listeners.add(listener); } @Override public void removeOnClearedListener(@NonNull OnClearedListener listener) { ThreadUtil.ensureMainThread(); throwIfOnClearedDispatched(); listeners.remove(listener); } void dispatchOnCleared() { ThreadUtil.ensureMainThread(); onClearedDispatched = true; for (OnClearedListener listener : listeners) { listener.onCleared(); } } private void throwIfOnClearedDispatched() { if (onClearedDispatched) { throw new IllegalStateException( "There was a race between the call to add/remove an OnClearedListener and onCleared(). " + "This can happen when posting to the Main thread from a background thread, " + "which is not supported."); } } } @Module @InstallIn(ActivityRetainedComponent.class) abstract static class LifecycleModule { @Binds abstract ActivityRetainedLifecycle bind(Lifecycle impl); } }