1 /*
2  * Copyright (C) 2017 The Dagger Authors.
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 dagger.android.support;
18 
19 import static android.util.Log.DEBUG;
20 import static dagger.internal.Preconditions.checkNotNull;
21 
22 import android.app.Activity;
23 import androidx.fragment.app.Fragment;
24 import android.util.Log;
25 import dagger.android.AndroidInjector;
26 import dagger.android.HasAndroidInjector;
27 import dagger.internal.Beta;
28 
29 /** Injects core Android types from support libraries. */
30 @Beta
31 public final class AndroidSupportInjection {
32   private static final String TAG = "dagger.android.support";
33 
34   /**
35    * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
36    * otherwise throws an {@link IllegalArgumentException}.
37    *
38    * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to
39    * use to inject {@code fragment}:
40    *
41    * <ol>
42    *   <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link
43    *       HasAndroidInjector}, and if none do
44    *   <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
45    *       {@link HasAndroidInjector}, and if not
46    *   <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}.
47    * </ol>
48    *
49    * If none of them implement {@link HasAndroidInjector}, a {@link IllegalArgumentException} is
50    * thrown.
51    *
52    * @throws IllegalArgumentException if no parent fragment, activity, or application implements
53    *     {@link HasAndroidInjector}.
54    */
inject(Fragment fragment)55   public static void inject(Fragment fragment) {
56     checkNotNull(fragment, "fragment");
57     HasAndroidInjector hasAndroidInjector = findHasAndroidInjectorForFragment(fragment);
58     if (Log.isLoggable(TAG, DEBUG)) {
59       Log.d(
60           TAG,
61           String.format(
62               "An injector for %s was found in %s",
63               fragment.getClass().getCanonicalName(),
64               hasAndroidInjector.getClass().getCanonicalName()));
65     }
66 
67     inject(fragment, hasAndroidInjector);
68   }
69 
inject(Object target, HasAndroidInjector hasAndroidInjector)70   private static void inject(Object target, HasAndroidInjector hasAndroidInjector) {
71     AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector();
72     checkNotNull(
73         androidInjector, "%s.androidInjector() returned null", hasAndroidInjector.getClass());
74 
75     androidInjector.inject(target);
76   }
77 
findHasAndroidInjectorForFragment(Fragment fragment)78   private static HasAndroidInjector findHasAndroidInjectorForFragment(Fragment fragment) {
79     Fragment parentFragment = fragment;
80     while ((parentFragment = parentFragment.getParentFragment()) != null) {
81       if (parentFragment instanceof HasAndroidInjector) {
82         return (HasAndroidInjector) parentFragment;
83       }
84     }
85     Activity activity = fragment.getActivity();
86     if (activity instanceof HasAndroidInjector) {
87       return (HasAndroidInjector) activity;
88     }
89     if (activity.getApplication() instanceof HasAndroidInjector) {
90       return (HasAndroidInjector) activity.getApplication();
91     }
92     throw new IllegalArgumentException(
93         String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
94   }
95 
AndroidSupportInjection()96   private AndroidSupportInjection() {}
97 }
98