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; 18 19 import static android.util.Log.DEBUG; 20 import static dagger.internal.Preconditions.checkNotNull; 21 22 import android.app.Activity; 23 import android.app.Application; 24 import android.app.Fragment; 25 import android.app.Service; 26 import android.content.BroadcastReceiver; 27 import android.content.ContentProvider; 28 import android.content.Context; 29 import android.util.Log; 30 import dagger.internal.Beta; 31 32 /** Injects core Android types. */ 33 @Beta 34 public final class AndroidInjection { 35 private static final String TAG = "dagger.android"; 36 37 /** 38 * Injects {@code activity} if an associated {@link AndroidInjector} implementation can be found, 39 * otherwise throws an {@link IllegalArgumentException}. 40 * 41 * @throws RuntimeException if the {@link Application} doesn't implement {@link 42 * HasAndroidInjector}. 43 */ inject(Activity activity)44 public static void inject(Activity activity) { 45 checkNotNull(activity, "activity"); 46 Application application = activity.getApplication(); 47 if (!(application instanceof HasAndroidInjector)) { 48 throw new RuntimeException( 49 String.format( 50 "%s does not implement %s", 51 application.getClass().getCanonicalName(), 52 HasAndroidInjector.class.getCanonicalName())); 53 } 54 55 inject(activity, (HasAndroidInjector) application); 56 } 57 58 /** 59 * Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found, 60 * otherwise throws an {@link IllegalArgumentException}. 61 * 62 * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to 63 * use to inject {@code fragment}: 64 * 65 * <ol> 66 * <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link 67 * HasAndroidInjector}, and if none do 68 * <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements 69 * {@link HasAndroidInjector}, and if not 70 * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}. 71 * </ol> 72 * 73 * If none of them implement {@link HasAndroidInjector}, a {@link IllegalArgumentException} is 74 * thrown. 75 * 76 * @throws IllegalArgumentException if no parent fragment, activity, or application implements 77 * {@link HasAndroidInjector}. 78 */ inject(Fragment fragment)79 public static void inject(Fragment fragment) { 80 checkNotNull(fragment, "fragment"); 81 HasAndroidInjector hasAndroidInjector = findHasAndroidInjectorForFragment(fragment); 82 if (Log.isLoggable(TAG, DEBUG)) { 83 Log.d( 84 TAG, 85 String.format( 86 "An injector for %s was found in %s", 87 fragment.getClass().getCanonicalName(), 88 hasAndroidInjector.getClass().getCanonicalName())); 89 } 90 91 inject(fragment, hasAndroidInjector); 92 } 93 findHasAndroidInjectorForFragment(Fragment fragment)94 private static HasAndroidInjector findHasAndroidInjectorForFragment(Fragment fragment) { 95 Fragment parentFragment = fragment; 96 while ((parentFragment = parentFragment.getParentFragment()) != null) { 97 if (parentFragment instanceof HasAndroidInjector) { 98 return (HasAndroidInjector) parentFragment; 99 } 100 } 101 Activity activity = fragment.getActivity(); 102 if (activity instanceof HasAndroidInjector) { 103 return (HasAndroidInjector) activity; 104 } 105 if (activity.getApplication() instanceof HasAndroidInjector) { 106 return (HasAndroidInjector) activity.getApplication(); 107 } 108 throw new IllegalArgumentException( 109 String.format("No injector was found for %s", fragment.getClass().getCanonicalName())); 110 } 111 112 /** 113 * Injects {@code service} if an associated {@link AndroidInjector} implementation can be found, 114 * otherwise throws an {@link IllegalArgumentException}. 115 * 116 * @throws RuntimeException if the {@link Application} doesn't implement {@link 117 * HasAndroidInjector}. 118 */ inject(Service service)119 public static void inject(Service service) { 120 checkNotNull(service, "service"); 121 Application application = service.getApplication(); 122 if (!(application instanceof HasAndroidInjector)) { 123 throw new RuntimeException( 124 String.format( 125 "%s does not implement %s", 126 application.getClass().getCanonicalName(), 127 HasAndroidInjector.class.getCanonicalName())); 128 } 129 130 inject(service, (HasAndroidInjector) application); 131 } 132 133 /** 134 * Injects {@code broadcastReceiver} if an associated {@link AndroidInjector} implementation can 135 * be found, otherwise throws an {@link IllegalArgumentException}. 136 * 137 * @throws RuntimeException if the {@link Application} from {@link 138 * Context#getApplicationContext()} doesn't implement {@link HasAndroidInjector}. 139 */ inject(BroadcastReceiver broadcastReceiver, Context context)140 public static void inject(BroadcastReceiver broadcastReceiver, Context context) { 141 checkNotNull(broadcastReceiver, "broadcastReceiver"); 142 checkNotNull(context, "context"); 143 Application application = (Application) context.getApplicationContext(); 144 if (!(application instanceof HasAndroidInjector)) { 145 throw new RuntimeException( 146 String.format( 147 "%s does not implement %s", 148 application.getClass().getCanonicalName(), 149 HasAndroidInjector.class.getCanonicalName())); 150 } 151 152 inject(broadcastReceiver, (HasAndroidInjector) application); 153 } 154 155 /** 156 * Injects {@code contentProvider} if an associated {@link AndroidInjector} implementation can be 157 * found, otherwise throws an {@link IllegalArgumentException}. 158 * 159 * @throws RuntimeException if the {@link Application} doesn't implement {@link 160 * HasAndroidInjector}. 161 */ inject(ContentProvider contentProvider)162 public static void inject(ContentProvider contentProvider) { 163 checkNotNull(contentProvider, "contentProvider"); 164 Application application = (Application) contentProvider.getContext().getApplicationContext(); 165 if (!(application instanceof HasAndroidInjector)) { 166 throw new RuntimeException( 167 String.format( 168 "%s does not implement %s", 169 application.getClass().getCanonicalName(), 170 HasAndroidInjector.class.getCanonicalName())); 171 } 172 173 inject(contentProvider, (HasAndroidInjector) application); 174 } 175 inject(Object target, HasAndroidInjector hasAndroidInjector)176 private static void inject(Object target, HasAndroidInjector hasAndroidInjector) { 177 AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector(); 178 checkNotNull( 179 androidInjector, "%s.androidInjector() returned null", hasAndroidInjector.getClass()); 180 181 androidInjector.inject(target); 182 } 183 AndroidInjection()184 private AndroidInjection() {} 185 } 186