Home | History | Annotate | Download | only in manager
      1 package com.bumptech.glide.manager;
      2 
      3 import android.annotation.TargetApi;
      4 import android.app.Activity;
      5 import android.app.Application;
      6 import android.content.Context;
      7 import android.content.ContextWrapper;
      8 import android.os.Build;
      9 import android.os.Handler;
     10 import android.os.Looper;
     11 import android.os.Message;
     12 import android.support.v4.app.Fragment;
     13 import android.support.v4.app.FragmentActivity;
     14 import android.support.v4.app.FragmentManager;
     15 import android.util.Log;
     16 
     17 import com.bumptech.glide.RequestManager;
     18 import com.bumptech.glide.util.Util;
     19 
     20 import java.util.HashMap;
     21 import java.util.Map;
     22 
     23 /**
     24  * A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or retrieving existing
     25  * ones from activities and fragment.
     26  */
     27 public class RequestManagerRetriever implements Handler.Callback {
     28     static final String TAG = "com.bumptech.glide.manager";
     29 
     30     /** The singleton instance of RequestManagerRetriever. */
     31     private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
     32 
     33     private static final int ID_REMOVE_FRAGMENT_MANAGER = 1;
     34     private static final int ID_REMOVE_SUPPORT_FRAGMENT_MANAGER = 2;
     35 
     36     /** The top application level RequestManager. */
     37     private volatile RequestManager applicationManager;
     38 
     39     // Visible for testing.
     40     /** Pending adds for RequestManagerFragments. */
     41     final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments =
     42             new HashMap<android.app.FragmentManager, RequestManagerFragment>();
     43 
     44     // Visible for testing.
     45     /** Pending adds for SupportRequestManagerFragments. */
     46     final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
     47             new HashMap<FragmentManager, SupportRequestManagerFragment>();
     48 
     49     /** Main thread handler to handle cleaning up pending fragment maps. */
     50     private final Handler handler;
     51 
     52     /**
     53      * Retrieves and returns the RequestManagerRetriever singleton.
     54      */
     55     public static RequestManagerRetriever get() {
     56         return INSTANCE;
     57     }
     58 
     59     // Visible for testing.
     60     RequestManagerRetriever() {
     61         handler = new Handler(Looper.getMainLooper(), this /* Callback */);
     62     }
     63 
     64     private RequestManager getApplicationManager(Context context) {
     65         // Either an application context or we're on a background thread.
     66         if (applicationManager == null) {
     67             synchronized (this) {
     68                 if (applicationManager == null) {
     69                     // Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
     70                     // However, in this case since the manager attached to the application will not receive lifecycle
     71                     // events, we must force the manager to start resumed using ApplicationLifecycle.
     72                     applicationManager = new RequestManager(context.getApplicationContext(),
     73                             new ApplicationLifecycle());
     74                 }
     75             }
     76         }
     77 
     78         return applicationManager;
     79     }
     80 
     81     public RequestManager get(Context context) {
     82         if (context == null) {
     83             throw new IllegalArgumentException("You cannot start a load on a null Context");
     84         } else if (Util.isOnMainThread() && !(context instanceof Application)) {
     85             if (context instanceof FragmentActivity) {
     86                 return get((FragmentActivity) context);
     87             } else if (context instanceof Activity) {
     88                 return get((Activity) context);
     89             } else if (context instanceof ContextWrapper) {
     90                 return get(((ContextWrapper) context).getBaseContext());
     91             }
     92         }
     93 
     94         return getApplicationManager(context);
     95     }
     96 
     97     public RequestManager get(FragmentActivity activity) {
     98         if (Util.isOnBackgroundThread()) {
     99             return get(activity.getApplicationContext());
    100         } else {
    101             assertNotDestroyed(activity);
    102             FragmentManager fm = activity.getSupportFragmentManager();
    103             return supportFragmentGet(activity, fm);
    104         }
    105     }
    106 
    107     public RequestManager get(Fragment fragment) {
    108         if (fragment.getActivity() == null) {
    109             throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
    110         }
    111         if (Util.isOnBackgroundThread()) {
    112             return get(fragment.getActivity().getApplicationContext());
    113         } else {
    114             if (fragment.isDetached()) {
    115                 throw new IllegalArgumentException("You cannot start a load on a detached fragment");
    116             }
    117             FragmentManager fm = fragment.getChildFragmentManager();
    118             return supportFragmentGet(fragment.getActivity(), fm);
    119         }
    120     }
    121 
    122     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    123     public RequestManager get(Activity activity) {
    124         if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    125             return get(activity.getApplicationContext());
    126         } else {
    127             assertNotDestroyed(activity);
    128             android.app.FragmentManager fm = activity.getFragmentManager();
    129             return fragmentGet(activity, fm);
    130         }
    131     }
    132 
    133     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    134     private static void assertNotDestroyed(Activity activity) {
    135         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
    136             throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
    137         }
    138     }
    139 
    140     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    141     public RequestManager get(android.app.Fragment fragment) {
    142         if (fragment.getActivity() == null) {
    143             throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
    144         }
    145         if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
    146             return get(fragment.getActivity().getApplicationContext());
    147         } else {
    148             assertNotDetached(fragment);
    149             android.app.FragmentManager fm = fragment.getChildFragmentManager();
    150             return fragmentGet(fragment.getActivity(), fm);
    151         }
    152     }
    153 
    154     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    155     private static void assertNotDetached(android.app.Fragment fragment) {
    156         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2 && fragment.isDetached()) {
    157             throw new IllegalArgumentException("You cannot start a load on a detached fragment");
    158         }
    159     }
    160 
    161     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    162     RequestManager fragmentGet(Context context, final android.app.FragmentManager fm) {
    163         RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(TAG);
    164         if (current == null) {
    165             current = pendingRequestManagerFragments.get(fm);
    166             if (current == null) {
    167                 current = new RequestManagerFragment();
    168                 pendingRequestManagerFragments.put(fm, current);
    169                 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
    170                 handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    171             }
    172         }
    173         RequestManager requestManager = current.getRequestManager();
    174         if (requestManager == null) {
    175             requestManager = new RequestManager(context, current.getLifecycle());
    176             current.setRequestManager(requestManager);
    177         }
    178         return requestManager;
    179 
    180     }
    181 
    182     RequestManager supportFragmentGet(Context context, final FragmentManager fm) {
    183         SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG);
    184         if (current == null) {
    185             current = pendingSupportRequestManagerFragments.get(fm);
    186             if (current == null) {
    187                 current = new SupportRequestManagerFragment();
    188                 pendingSupportRequestManagerFragments.put(fm, current);
    189                 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
    190                 handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
    191             }
    192         }
    193         RequestManager requestManager = current.getRequestManager();
    194         if (requestManager == null) {
    195             requestManager = new RequestManager(context, current.getLifecycle());
    196             current.setRequestManager(requestManager);
    197         }
    198         return requestManager;
    199     }
    200 
    201     @Override
    202     public boolean handleMessage(Message message) {
    203         boolean handled = true;
    204         Object removed = null;
    205         Object key = null;
    206         switch (message.what) {
    207             case ID_REMOVE_FRAGMENT_MANAGER:
    208                 android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
    209                 key = fm;
    210                 removed = pendingRequestManagerFragments.remove(fm);
    211                 break;
    212             case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
    213                 FragmentManager supportFm = (FragmentManager) message.obj;
    214                 key = supportFm;
    215                 removed = pendingSupportRequestManagerFragments.remove(supportFm);
    216                 break;
    217             default:
    218                 handled = false;
    219         }
    220         if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) {
    221             Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key);
    222         }
    223         return handled;
    224     }
    225 }
    226