| 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<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments = |
| new HashMap<android.app.FragmentManager, RequestManagerFragment>(); |
| |
| // Visible for testing. |
| /** Pending adds for SupportRequestManagerFragments. */ |
| final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments = |
| new HashMap<FragmentManager, SupportRequestManagerFragment>(); |
| |
| /** 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; |
| } |
| } |