package roboguice.inject; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.WeakHashMap; import roboguice.fragment.FragmentUtil; import com.google.inject.MembersInjector; import com.google.inject.Provider; import com.google.inject.spi.TypeEncounter; import android.app.Activity; import android.content.Context; /** * This class gets twice as many providers as necessary to do its job, look into optimizations in the future if this is a bottleneck */ public class FragmentMembersInjector<T> implements MembersInjector<T> { protected static final WeakHashMap<Object,ArrayList<FragmentMembersInjector<?>>> VIEW_MEMBERS_INJECTORS = new WeakHashMap<Object, ArrayList<FragmentMembersInjector<?>>>(); protected Field field; protected Annotation annotation; protected WeakReference<T> instanceRef; @SuppressWarnings("rawtypes") protected FragmentUtil.f fragUtils; @SuppressWarnings("rawtypes") protected Provider fragManager; protected Provider<Activity> activityProvider; public FragmentMembersInjector(Field field, Annotation annotation, TypeEncounter<T> typeEncounter, FragmentUtil.f<?,?> utils) { this.field = field; this.annotation = annotation; this.activityProvider = typeEncounter.getProvider(Activity.class); if( utils !=null ) { this.fragUtils = utils; this.fragManager = typeEncounter.getProvider(utils.fragmentManagerType()); } } /** * This is called when instance is injected by guice. Because the views may or may not be set up yet, * we don't do the real view injection until later. * * @param instance the instance being injected by guice */ public void injectMembers(T instance) { synchronized (FragmentMembersInjector.class) { boolean isValidFragment = fragUtils != null && fragUtils.fragmentType().isInstance(instance); final Object key = isValidFragment ? instance : activityProvider.get(); if( key==null ) return; // Add a view injector for the key ArrayList<FragmentMembersInjector<?>> injectors = VIEW_MEMBERS_INJECTORS.get(key); if( injectors==null ) { injectors = new ArrayList<FragmentMembersInjector<?>>(); VIEW_MEMBERS_INJECTORS.put(key, injectors); } injectors.add(this); this.instanceRef = new WeakReference<T>(instance); } } /** * This is when the view references are actually evaluated. * @param activityOrFragment an activity or fragment */ @SuppressWarnings("unchecked") public void reallyInjectMembers( Object activityOrFragment ) { final T instance = instanceRef.get(); if( instance==null ) return; if( activityOrFragment instanceof Context && !(activityOrFragment instanceof Activity )) throw new UnsupportedOperationException("Can't inject fragment into a non-Activity context"); Object fragment = null; try { final InjectFragment injectFragment = (InjectFragment) annotation; final int id = injectFragment.value(); if( id>=0 ) fragment = fragUtils.findFragmentById(fragManager.get(), id); else fragment = fragUtils.findFragmentByTag(fragManager.get(),injectFragment.tag()); if (fragment == null && Nullable.notNullable(field)) throw new NullPointerException(String.format("Can't inject null value into %s.%s when field is not @Nullable", field.getDeclaringClass(), field.getName())); field.setAccessible(true); field.set(instance, fragment); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (IllegalArgumentException f) { throw new IllegalArgumentException(String.format("Can't assign %s value %s to %s field %s", fragment != null ? fragment.getClass() : "(null)", fragment, field.getType(), field.getName()), f); } } protected static void injectViews(Object activityOrFragment) { synchronized ( FragmentMembersInjector.class ) { final ArrayList<FragmentMembersInjector<?>> injectors = VIEW_MEMBERS_INJECTORS.get(activityOrFragment); if(injectors!=null) for(FragmentMembersInjector<?> viewMembersInjector : injectors) viewMembersInjector.reallyInjectMembers(activityOrFragment); } } }