package com.bumptech.glide.manager; import android.annotation.TargetApi; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.ContextWrapper; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.Log; import com.bumptech.glide.RequestManager; import com.bumptech.glide.util.Util; import java.util.HashMap; import java.util.Map; /** * A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or retrieving existing * ones from activities and fragment. */ public class RequestManagerRetriever implements Handler.Callback { static final String TAG = "com.bumptech.glide.manager"; /** The singleton instance of RequestManagerRetriever. */ private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever(); private static final int ID_REMOVE_FRAGMENT_MANAGER = 1; private static final int ID_REMOVE_SUPPORT_FRAGMENT_MANAGER = 2; /** The top application level RequestManager. */ private volatile RequestManager applicationManager; // Visible for testing. /** Pending adds for RequestManagerFragments. */ final Map pendingRequestManagerFragments = new HashMap(); // Visible for testing. /** Pending adds for SupportRequestManagerFragments. */ final Map pendingSupportRequestManagerFragments = new HashMap(); /** Main thread handler to handle cleaning up pending fragment maps. */ private final Handler handler; /** * Retrieves and returns the RequestManagerRetriever singleton. */ public static RequestManagerRetriever get() { return INSTANCE; } // Visible for testing. RequestManagerRetriever() { handler = new Handler(Looper.getMainLooper(), this /* Callback */); } private RequestManager getApplicationManager(Context context) { // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or activity. // However, in this case since the manager attached to the application will not receive lifecycle // events, we must force the manager to start resumed using ApplicationLifecycle. applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle()); } } } return applicationManager; } public RequestManager get(Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } public RequestManager get(FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } } public RequestManager get(Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread()) { return get(fragment.getActivity().getApplicationContext()); } else { if (fragment.isDetached()) { throw new IllegalArgumentException("You cannot start a load on a detached fragment"); } FragmentManager fm = fragment.getChildFragmentManager(); return supportFragmentGet(fragment.getActivity(), fm); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private static void assertNotDestroyed(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) { throw new IllegalArgumentException("You cannot start a load for a destroyed activity"); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public RequestManager get(android.app.Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return get(fragment.getActivity().getApplicationContext()); } else { assertNotDetached(fragment); android.app.FragmentManager fm = fragment.getChildFragmentManager(); return fragmentGet(fragment.getActivity(), fm); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) private static void assertNotDetached(android.app.Fragment fragment) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2 && fragment.isDetached()) { throw new IllegalArgumentException("You cannot start a load on a detached fragment"); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, final android.app.FragmentManager fm) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle()); current.setRequestManager(requestManager); } return requestManager; } RequestManager supportFragmentGet(Context context, final FragmentManager fm) { SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG); if (current == null) { current = pendingSupportRequestManagerFragments.get(fm); if (current == null) { current = new SupportRequestManagerFragment(); pendingSupportRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget(); } } RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle()); current.setRequestManager(requestManager); } return requestManager; } @Override public boolean handleMessage(Message message) { boolean handled = true; Object removed = null; Object key = null; switch (message.what) { case ID_REMOVE_FRAGMENT_MANAGER: android.app.FragmentManager fm = (android.app.FragmentManager) message.obj; key = fm; removed = pendingRequestManagerFragments.remove(fm); break; case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER: FragmentManager supportFm = (FragmentManager) message.obj; key = supportFm; removed = pendingSupportRequestManagerFragments.remove(supportFm); break; default: handled = false; } if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key); } return handled; } }